相対パスを指定して絶対パスを取得するコマンドはありますか?
たとえば、$lineにdir内の各ファイルの絶対パスを含める必要があります./etc/
find ./ -type f | while read line; do
echo $line
done
試してみてくださいrealpath
。
~ $ sudo apt-get install realpath # may already be installed
~ $ realpath .bashrc
/home/username/.bashrc
シンボリックリンクの拡張を回避するには、を使用しますrealpath -s
。
答えは「ファイルへの絶対パスを出力するbash/fishコマンド」から来ています。
coreutilsパッケージがインストールされている場合は、通常readlink -f relative_file_name
、絶対パッケージを取得するために使用できます(すべてのシンボリックリンクが解決されています)
#! /bin/sh
echo "$(cd "$(dirname "$1")"; pwd)/$(basename "$1")"
UPDいくつかの説明
"$1"
dirname "$1"
cd "$(dirname "$1")
を実行して絶対パスを取得しますpwd
$(basename "$1")
echo
として使用する:
find "$(pwd)"/ -type f
すべてのファイルを取得する、または
echo "$(pwd)/$line"
フルパスを表示するには(相対パスが重要な場合)
その価値について、私は選ばれた答えに投票しましたが、解決策を共有したいと思いました。欠点は、Linuxのみであるということです。スタックオーバーフローが発生する前に、OSXに相当するものを見つけるために約5分を費やしました。私はそれがそこにあると確信しています。
readlink -e
Linuxでは、と組み合わせて使用できますdirname
。
$(dirname $(readlink -e ../../../../etc/passwd))
収量
/etc/
次に、dirname
の姉妹を使用basename
して、ファイル名を取得します
$(basename ../../../../../passwd)
収量
passwd
それをすべてまとめてください。
F=../../../../../etc/passwd
echo "$(dirname $(readlink -e $F))/$(basename $F)"
収量
/etc/passwd
ディレクトリをターゲットにしている場合は安全で、basename
何も返さず、最終的な出力で2つのスラッシュが表示されます。
これが最もポータブルだと思います:
abspath() {
cd "$(dirname "$1")"
printf "%s/%s\n" "$(pwd)" "$(basename "$1")"
cd "$OLDPWD"
}
ただし、パスが存在しない場合は失敗します。
realpath
おそらく最高です
だが ...
最初の質問は最初は非常に混乱しており、例として述べたように質問との関連性はほとんどありませんでした。
選択された回答は実際に与えられた例に回答し、タイトルの質問にはまったく回答しません。最初のコマンドはその答えであり(本当にそうですか?私は疑っています)、「/」がなくても同様に実行できます。そして、2番目のコマンドが何をしているのかわかりません。
いくつかの問題が混在しています:
相対パス名を絶対パス名に変更することは、それが何を意味するかにかかわらず、おそらく何もしません。(通常、などのコマンドを発行する場合は、ファイルが実際に作成される前にtouch foo/bar
、パス名foo/bar
が存在している必要があり、場合によっては計算で使用される必要があります。)
特にパス上のシンボリックリンク(symlinks)のために、同じファイル(または潜在的なファイル)を示す複数の絶対パス名が存在する場合がありますが、他の理由である可能性があります(デバイスは読み取り専用として2回マウントされる場合があります)。そのようなシンボリックリンクを明示的に解決したい場合としたくない場合があります。
非シンボリックリンクファイルまたは名前へのシンボリックリンクのチェーンの最後に到達します。これは、実行方法に応じて、絶対パス名を生成する場合と生成しない場合があります。そして、それを絶対パス名に解決したいかもしれません。
readlink foo
オプションのないコマンドfoo
は、引数がシンボリックリンクであり、その回答がそのシンボリックリンクの値である場合にのみ回答を提供します。他のリンクはたどられません。答えは相対パスかもしれません:シンボリックリンク引数の値が何であれ。
ただし、readlink
すべてのファイルで機能するオプション(-f -eまたは-m)があり、引数で実際に示されているファイルに1つの絶対パス名(シンボリックリンクのないパス名)を指定します。
これは、シンボリックリンクではないものには問題なく機能しますが、パス上の中間シンボリックリンクを解決せずに絶対パス名を使用したい場合があります。これはコマンドによって行われますrealpath -s foo
シンボリックリンク引数の場合、readlink
そのオプションを使用すると、引数への絶対パス上のすべてのシンボリックリンクが再び解決されますが、引数値に従うことで検出される可能性のあるすべてのシンボリックリンクも含まれます。引数のシンボリックリンク自体への絶対パスが必要な場合は、リンク先のパスではなく、それを望まない場合があります。繰り返しますfoo
が、がシンボリックリンクの場合realpath -s foo
、引数として指定されたものを含め、シンボリックリンクを解決せずに絶対パスを取得します。
-s
オプションがない場合は、リンクの値を読み取るだけでなく、他のいくつかのことを除いて、realpath
とほとんど同じ
です。readlink
なぜreadlink
そのオプションがあるのかは私にはわかりませんが、明らかに望ましくない冗長性を作成します
realpath
。
システム間でいくつかのバリエーションがあるかもしれないことを除いて、ウェブを探索することはそれほど多くを語りません。
結論:realpath
少なくともここで要求された使用法では、最も柔軟性があり、使用するのに最適なコマンドです。
私のお気に入りのソリューションは、 @ EugenKonkovによるものでした。これは、他のユーティリティ(coreutilsパッケージ)の存在を意味するものではなかったためです。
しかし、相対パス「。」では失敗しました。および「..」なので、これらの特殊なケースを処理するわずかに改善されたバージョンがあります。
cd
ただし、ユーザーが相対パスの親ディレクトリにアクセスする権限を持っていない場合でも失敗します。
#! /bin/sh
# Takes a path argument and returns it as an absolute path.
# No-op if the path is already absolute.
function to-abs-path {
local target="$1"
if [ "$target" == "." ]; then
echo "$(pwd)"
elif [ "$target" == ".." ]; then
echo "$(dirname "$(pwd)")"
else
echo "$(cd "$(dirname "$1")"; pwd)/$(basename "$1")"
fi
}
ユーゲンの答えは私にはうまくいきませんでしたが、これはうまくいきました:
absolute="$(cd $(dirname \"$file\"); pwd)/$(basename \"$file\")"
ちなみに、現在の作業ディレクトリは影響を受けません。
最良の解決策であるimhoは、https ://stackoverflow.com/a/3373298/9724628に投稿されたものです。
動作するにはPythonが必要ですが、すべてまたはほとんどのエッジケースをカバーし、非常にポータブルなソリューションのようです。
python -c "import os,sys; print(os.path.realpath(sys.argv[1]))" path/to/file
python -c "import os,sys; print(os.path.abspath(sys.argv[1]))" path/to/file
@ernest-aのかなり素晴らしいバージョンの改善:
absolute_path() {
cd "$(dirname "$1")"
case $(basename $1) in
..) echo "$(dirname $(pwd))";;
.) echo "$(pwd)";;
*) echo "$(pwd)/$(basename $1)";;
esac
}
これは、パスの最後の要素がである場合を正しく処理します。..
この場合、"$(pwd)/$(basename "$1")"
@ernest-aの答えはとして渡されaccurate_sub_path/spurious_subdirectory/..
ます。
の場合、find
検索するための絶対パスを指定するのがおそらく最も簡単です。例:
find /etc
find `pwd`/subdir_of_current_dir/ -type f
リアルパスが存在せず、そのreadlinkが絶対パスを印刷できないMac OS Xでbashを使用している場合は、独自のバージョンをコーディングして印刷するしかありません。これが私の実装です:
(純粋なbash)
abspath(){
local thePath
if [[ ! "$1" =~ ^/ ]];then
thePath="$PWD/$1"
else
thePath="$1"
fi
echo "$thePath"|(
IFS=/
read -a parr
declare -a outp
for i in "${parr[@]}";do
case "$i" in
''|.) continue ;;
..)
len=${#outp[@]}
if ((len==0));then
continue
else
unset outp[$((len-1))]
fi
;;
*)
len=${#outp[@]}
outp[$len]="$i"
;;
esac
done
echo /"${outp[*]}"
)
}
(gawkを使用)
abspath_gawk() {
if [[ -n "$1" ]];then
echo $1|gawk '{
if(substr($0,1,1) != "/"){
path = ENVIRON["PWD"]"/"$0
} else path = $0
split(path, a, "/")
n = asorti(a, b,"@ind_num_asc")
for(i in a){
if(a[i]=="" || a[i]=="."){
delete a[i]
}
}
n = asorti(a, b, "@ind_num_asc")
m = 0
while(m!=n){
m = n
for(i=1;i<=n;i++){
if(a[b[i]]==".."){
if(b[i-1] in a){
delete a[b[i-1]]
delete a[b[i]]
n = asorti(a, b, "@ind_num_asc")
break
} else exit 1
}
}
}
n = asorti(a, b, "@ind_num_asc")
if(n==0){
printf "/"
} else {
for(i=1;i<=n;i++){
printf "/"a[b[i]]
}
}
}'
fi
}
(純粋なbsd awk)
#!/usr/bin/env awk -f
function abspath(path, i,j,n,a,b,back,out){
if(substr(path,1,1) != "/"){
path = ENVIRON["PWD"]"/"path
}
split(path, a, "/")
n = length(a)
for(i=1;i<=n;i++){
if(a[i]==""||a[i]=="."){
continue
}
a[++j]=a[i]
}
for(i=j+1;i<=n;i++){
delete a[i]
}
j=0
for(i=length(a);i>=1;i--){
if(back==0){
if(a[i]==".."){
back++
continue
} else {
b[++j]=a[i]
}
} else {
if(a[i]==".."){
back++
continue
} else {
back--
continue
}
}
}
if(length(b)==0){
return "/"
} else {
for(i=length(b);i>=1;i--){
out=out"/"b[i]
}
return out
}
}
BEGIN{
if(ARGC>1){
for(k=1;k<ARGC;k++){
print abspath(ARGV[k])
}
exit
}
}
{
print abspath($0)
}
例:
$ abspath I/am/.//..//the/./god/../of///.././war
/Users/leon/I/the/war
@ ernest-aの答えに似てい$OLDPWD
ますが、新しい関数に影響を与えたり定義したりせずに、サブシェルを起動できます(cd <path>; pwd)
$ pwd
/etc/apache2
$ cd ../cups
$ cd -
/etc/apache2
$ (cd ~/..; pwd)
/Users
$ cd -
/etc/cups
プログラムをインストールする必要がないので、EugenKonkovの答えが最良であることがわかりました。ただし、存在しないディレクトリでは失敗します。
存在しないディレクトリで機能する関数を作成しました。
function getRealPath()
{
local -i traversals=0
currentDir="$1"
basename=''
while :; do
[[ "$currentDir" == '.' ]] && { echo "$1"; return 1; }
[[ $traversals -eq 0 ]] && pwd=$(cd "$currentDir" 2>&1 && pwd) && { echo "$pwd/$basename"; return 0; }
currentBasename="$(basename "$currentDir")"
currentDir="$(dirname "$currentDir")"
[[ "$currentBasename" == '..' ]] && (( ++traversals )) || { [[ traversals -gt 0 ]] && (( traversals-- )) || basename="$currentBasename/$basename"; }
done
}
成功するdirname
までトラバースし、現在のディレクトリとによって削除されたすべてのものを返すことにより、存在しないディレクトリの問題を解決します。cd
dirname
find $PWD
または(bashで)を除いて、彼らが言っfind ~+
たことはもう少し便利です。
相対パスがディレクトリパスの場合は、私のものを試してみてください。これが最適です。
absPath=$(pushd ../SOME_RELATIVE_PATH_TO_Directory > /dev/null && pwd && popd > /dev/null)
echo $absPath
echo "mydir/doc/ mydir/usoe ./mydir/usm" | awk '{ split($0,array," "); for(i in array){ system("cd "array[i]" && echo $PWD") } }'
任意の相対パス$lineにbash文字列置換を使用できます。
line=$(echo ${line/#..\//`cd ..; pwd`\/})
line=$(echo ${line/#.\//`pwd`\/})
echo $line
基本的な文字列の前部置換は、式
$ {string /#substring / replacement}に従います。これについては、 https
://www.tldp.org/LDP/abs/html/string-manipulation.htmlで詳しく説明しています。
文字は、検索/置換する文字列の一部にしたい場合に\
無効にします。/
Mac OS Catalina、Ubuntu 16、Centos 7の間でうまく移植できるソリューションを見つけることができなかったので、Pythonインラインでそれを行うことにしました。これは、bashスクリプトでうまく機能しました。
to_abs_path() {
python -c "import os; print os.path.abspath('$1')"
}
to_abs_path "/some_path/../secrets"
readlink
これは、POSIXシェルとセマンティクスのみを使用して、任意の入力パスを完全に絶対化および正規化するために使用できるかなり短い関数です。
canonicalize_path() {
(
FILEPATH="$1"
for _ in 1 2 3 4 5 6 7 8; # Maximum symlink recursion depth
do
cd -L "`case "${FILEPATH}" in */*) echo "${FILEPATH%/*}";; *) echo ".";; esac`/" # cd $(dirname)
if ! FILEPATH="$(readlink "${FILEPATH##*/}" || ( echo "${FILEPATH##*/}" && false ) )";
then
break
fi
done
cd -P "." || return $?
echo "$(pwd)/${FILEPATH}"
)
}
参照ファイルが存在しない場合は、最終ファイル名に至るまでのディレクトリパスのみが解決されます。ファイルパスにつながるディレクトリのいずれかが存在しない場合、cd
エラーが返されます。readlink -f
これは、模倣しようとするGNU/Linuxコマンドの正確なセマンティクスです。
{1..8}
bash / zshでは、数値のリストをちょうどまたは類似のものに圧縮することもできます。Linuxでは、バージョン4.2でパス全体の合計解像度が40に変更される前の長年の上限であったため、8の数が選択されました。解像度の制限に達した場合、コードは失敗しませんが、代わりに最後に考慮されたパスを返します–[ -L "${FILEPATH}" ]
ただし、この状態を検出するために明示的なチェックを追加できます。
このコードは、関数とサブシェルラッパーを削除するだけで、現在の作業ディレクトリがファイルシステム内の実行されたスクリプトの場所(シェルスクリプトの一般的な要件)と一致することを確認するために簡単に再利用することもできます。
FILEPATH="$0"
for _ in 1 2 3 4 5 6 7 8; # Maximum symlink recursion depth
do
cd -L "`case "${FILEPATH}" in */*) echo "${FILEPATH%/*}";; *) echo ".";; esac`/" # cd $(dirname)
if ! FILEPATH="$(readlink "${FILEPATH##*/}" || ( echo "${FILEPATH##*/}" && false ) )";
then
break
fi
done
cd -P "."
FILEPATH="$(pwd)/${FILEPATH}"
相対パスを含む変数を絶対パスに変換する場合、これは機能します。
dir=`cd "$dir"`
ここではサブシェルで実行されるため、「cd」は作業ディレクトリを変更せずにエコーします。
これは他のすべてからの連鎖ソリューションです。たとえば、realpath
インストールされていないか、エラーコードで終了したために失敗した場合、パスが正しくなるまで次のソリューションが試行されます。
#!/bin/bash
function getabsolutepath() {
local target;
local changedir;
local basedir;
local firstattempt;
target="${1}";
if [ "$target" == "." ];
then
printf "%s" "$(pwd)";
elif [ "$target" == ".." ];
then
printf "%s" "$(dirname "$(pwd)")";
else
changedir="$(dirname "${target}")" && basedir="$(basename "${target}")" && firstattempt="$(cd "${changedir}" && pwd)" && printf "%s/%s" "${firstattempt}" "${basedir}" && return 0;
firstattempt="$(readlink -f "${target}")" && printf "%s" "${firstattempt}" && return 0;
firstattempt="$(realpath "${target}")" && printf "%s" "${firstattempt}" && return 0;
# If everything fails... TRHOW PYTHON ON IT!!!
local fullpath;
local pythoninterpreter;
local pythonexecutables;
local pythonlocations;
pythoninterpreter="python";
declare -a pythonlocations=("/usr/bin" "/bin");
declare -a pythonexecutables=("python" "python2" "python3");
for path in "${pythonlocations[@]}";
do
for executable in "${pythonexecutables[@]}";
do
fullpath="${path}/${executable}";
if [[ -f "${fullpath}" ]];
then
# printf "Found ${fullpath}\\n";
pythoninterpreter="${fullpath}";
break;
fi;
done;
if [[ "${pythoninterpreter}" != "python" ]];
then
# printf "Breaking... ${pythoninterpreter}\\n"
break;
fi;
done;
firstattempt="$(${pythoninterpreter} -c "import os, sys; print( os.path.abspath( sys.argv[1] ) );" "${target}")" && printf "%s" "${firstattempt}" && return 0;
# printf "Error: Could not determine the absolute path!\\n";
return 1;
fi
}
printf "\\nResults:\\n%s\\nExit: %s\\n" "$(getabsolutepath "./asdfasdf/ asdfasdf")" "${?}"
@EugenKonkovによるこの回答と@HashChangeによるこの回答に基づいて、私の回答は前者の簡潔さと後者の処理を組み合わせたものです。以下のすべてのオプションは、基本的なシェルコマンド言語POSIX標準にのみ依存していると思います。.
..
dirname
およびを使用basename
する場合のオプションは次のとおりです。
absPathDirname()
{
[ -d "${1}" ] && set -- "${1}" || set -- "`dirname "${1}"`" "/`basename "${1}"`"
echo "`cd "${1}"; pwd`${2}";
}
dirname
またはを使用しないbasename
場合の別の簡単なオプションは次のとおりです。
absPathMinusD()
{
[ -d "${1}" ] && set -- "${1}" || set -- "${1%${1##*/}}" "/${1##*/}"
echo "`cd "${1:-.}"; pwd`${2}";
}
上記の2つのオプションのいずれかをお勧めします。残りは楽しみのためだけです...
Grepバージョン:
absPathGrep()
{
echo "`[ "${1##/*}" ] && echo "$1" | grep -Eo '^(.*/)?\.\.($|/)' | { read d && cd "$d"; echo "${PWD}/${1#$d}"; } || echo "$1"`"
}
「シェルの制限された正規表現で何ができるか」の興味深い例として:
absPathShellReplace()
{
E="${1##*/}"; D="${E#$E${E#.}}"; DD="${D#$D${D#..}}"
DIR="${1%$E}${E#$DD}"; FILE="${1#$DIR}"; SEP=${FILE:+/}
echo "`cd "${DIR:-.}"; pwd`${SEP#$DIR}$FILE"
}