に変身/foo/bar/..
したい/foo
これを行うbashコマンドはありますか?
編集:私の実際のケースでは、ディレクトリは存在します。
/foo/bar/..
し/foo
、bash
コマンドを使用します。記載されていない他の要件がある場合は、おそらく彼らは...する必要があります
に変身/foo/bar/..
したい/foo
これを行うbashコマンドはありますか?
編集:私の実際のケースでは、ディレクトリは存在します。
/foo/bar/..
し/foo
、bash
コマンドを使用します。記載されていない他の要件がある場合は、おそらく彼らは...する必要があります
回答:
パスからファイル名の一部を切り詰めたい場合は、「dirname」と「basename」がお友達で、「realpath」も便利です。
dirname /foo/bar/baz
# /foo/bar
basename /foo/bar/baz
# baz
dirname $( dirname /foo/bar/baz )
# /foo
realpath ../foo
# ../foo: No such file or directory
realpath /tmp/../tmp/../tmp
# /tmp
realpath
代替案
realpath
シェルでサポートされていない場合は、試すことができます
readlink -f /path/here/..
また
readlink -m /path/there/../../
と同じように機能します
realpath -s /path/here/../../
正規化するためにパスが存在する必要はありません。
realpath
とreadlink
そうおそらくあなたはどちらかまたは両方なし得る、GNUコアユーティリティから来ています。私の記憶が正しければ、MacのreadlinkバージョンはGNUバージョンとは少し異なります:-\
これを行う直接のbashコマンドがあるかどうかはわかりませんが、通常は
normalDir="`cd "${dirToNormalize}";pwd`"
echo "${normalDir}"
そしてそれはうまくいきます。
rm -rf $normalDir
dirToNormalizeが存在しない場合、これは非常に危険です()。
&&
@ DavidBlevinsのコメントのとおりに使用するのがおそらく最善です。
お試しくださいrealpath
。以下はソース全体です。これにより、パブリックドメインに寄付されます。
// realpath.c: display the absolute path to a file or directory.
// Adam Liss, August, 2007
// This program is provided "as-is" to the public domain, without express or
// implied warranty, for any non-profit use, provided this notice is maintained.
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <libgen.h>
#include <limits.h>
static char *s_pMyName;
void usage(void);
int main(int argc, char *argv[])
{
char
sPath[PATH_MAX];
s_pMyName = strdup(basename(argv[0]));
if (argc < 2)
usage();
printf("%s\n", realpath(argv[1], sPath));
return 0;
}
void usage(void)
{
fprintf(stderr, "usage: %s PATH\n", s_pMyName);
exit(1);
}
realpath
。それはたまたま私のお気に入りですが、ソースからツールをコンパイルする代わりに。それはすべてヘビー級であり、たいていの場合あなたが望むだけですreadlink -f
...ところで、readlink
また、bashの組み込みではなくcoreutils
、Ubuntuの一部です。
ポータブルで信頼性の高いソリューションは、Pythonを使用することです。これは、ほとんどすべての場所(Darwinを含む)にプリインストールされています。次の2つのオプションがあります。
abspath
絶対パスを返しますが、シンボリックリンクを解決しません:
python -c "import os,sys; print(os.path.abspath(sys.argv[1]))" path/to/file
realpath
絶対パスを返し、その際にシンボリックリンクを解決し、正規パスを生成します。
python -c "import os,sys; print(os.path.realpath(sys.argv[1]))" path/to/file
いずれの場合path/to/file
も、相対パスまたは絶対パスのいずれかになります。
coreutilsパッケージのreadlinkユーティリティを使用します。
MY_PATH=$(readlink -f "$0")
readlink
絶対パスを取得するためのbash標準です。また、パスまたはパスが存在しない場合にフラグを指定すると、空の文字列が返されるという利点もあります。
存在するかもしれないし存在しないかもしれないが、親は存在するディレクトリへの絶対パスを取得するには、以下を使用します。
abspath=$(readlink -f $path)
すべての親と一緒に存在しなければならないディレクトリへの絶対パスを取得するには:
abspath=$(readlink -e $path)
指定されたパスを正規化し、存在する場合はシンボリックリンクをたどりますが、それ以外の場合は欠落しているディレクトリを無視し、とにかくパスを返すだけです。
abspath=$(readlink -m $path)
唯一の欠点は、readlinkがリンクをたどることです。リンクをたどらない場合は、次の代替規則を使用できます。
abspath=$(cd ${path%/*} && echo $PWD/${path##*/})
これは、$ pathのディレクトリ部分にchdirし、$ pathのファイル部分と共に現在のディレクトリを出力します。chdirに失敗すると、空の文字列とstderrでエラーが発生します。
readlink
それが利用可能な場合は、良いオプションです。OS Xバージョンは-e
または-f
オプションをサポートしていません。最初の3つの例では$path
、ファイル名のスペースまたはワイルドカードを処理するために、二重引用符で囲む必要があります。パラメータ展開の+1ですが、これにはセキュリティ上の脆弱性があります。path
が空の場合、これはcd
ホームディレクトリになります。二重引用符が必要です。abspath=$(cd "${path%/*}" && echo "$PWD/${path##*/}")
古い質問ですが、シェルレベルでフルパス名を処理する場合、もっと簡単な方法があります。
abspath = "$(cd" $ path "&& pwd)"
cdはサブシェルで発生するため、メインスクリプトには影響しません。
シェル組み込みコマンドが-Lと-Pを受け入れるとすると、2つのバリエーションがあります。
abspath = "$(cd -P" $ path "&& pwd -P)"#解決されたシンボリックリンクを含む物理パス abspath = "$(cd -L" $ path "&& pwd -L)"#シンボリックリンクを保持する論理パス
個人的には、何らかの理由でシンボリックリンクに魅了されない限り、この後者のアプローチはほとんど必要ありません。
参考:スクリプトが現在のディレクトリを後で変更した場合でも機能するスクリプトの開始ディレクトリを取得する方法のバリエーション。
name0 = "$(basename" $ 0 ")"; #スクリプトのベース名 dir0 = "$(cd" $(dirname "$ 0") "&& pwd)"; #絶対開始ディレクトリ
CDを使用すると、スクリプトが./script.shなどのコマンドによって実行され、cd / pwdを使用せずに..
Adam Lissが述べたようにrealpath
、すべてのディストリビューションにバンドルされているわけではありません。これが最善の解決策なので、これは残念です。提供されているソースコードは素晴らしいので、おそらく今から使い始めるでしょう。これは私が今まで使用してきたものですが、完全を期すためにここで共有します。
get_abs_path() {
local PARENT_DIR=$(dirname "$1")
cd "$PARENT_DIR"
local ABS_PATH="$(pwd)"/"$(basename "$1")"
cd - >/dev/null
echo "$ABS_PATH"
}
シンボリックリンクを解決したい場合は、単にに置き換えpwd
てくださいpwd -P
。
pwd -P
この場合のオプションの1つの問題... $(basename "$1")
別のディレクトリにあるファイルへのシンボリックリンクがどうなるかを検討してください。pwd -P
唯一のベース名部分パスのディレクトリ部分にシンボリックリンクを解決しますが、ありません。
私の最近の解決策は:
pushd foo/bar/..
dir=`pwd`
popd
Tim Whitcombの回答に基づいています。
pushd $(dirname /usr/bin/java)
、試してみてください。
正確な回答ではなく、おそらくフォローアップの質問(元の質問は明確ではなかった):
readlink
実際にシンボリックリンクを追跡したい場合は問題ありません。単に正規化するためのユースケースもある./
し、../
そして//
、純粋に構文的に行うことができる配列、ことなくシンボリックリンクをcanonicalizingは。readlink
これは良くありませんrealpath
。
for f in $paths; do (cd $f; pwd); done
既存のパスでは機能しますが、他のパスでは機能しません。
sed
(スクリプトは、繰り返しシーケンスを置き換えることはできませんことを除いて、良い賭けのように見えるだろう/foo/bar/baz/../..
- > /foo/bar/..
> - /foo
すべてのシステム上で前提としても安全ではないPerlの、のようなものを使用して、またはの出力を比較するために、いくつかの醜いループを使用せず)sed
へのその入力。
FWIW、Javaを使用したワンライナー(JDK 6+):
jrunscript -e 'for (var i = 0; i < arguments.length; i++) {println(new java.io.File(new java.io.File(arguments[i]).toURI().normalize()))}' $paths
realpath
には、-s
シンボリックリンクを解決せず、への参照のみを解決し/./
、/../
余分な/
文字を削除するオプションがあります。-m
オプションと組み合わせると、realpath
ファイル名のみを操作し、実際のファイルには影響しません。それは完璧なソリューションのように聞こえます。しかし、悲しいかな、realpath
まだ多くのシステムで欠落しています。
..
シンボリックリンクが含まれている場合、コンポーネントの削除は構文的に実行できません。 /one/two/../three
がへのシンボリックリンクである/one/three
場合two
と同じではありません/foo/bar
。
私はパーティーに遅れましたが、これは私がこのようなスレッドの束を読んだ後に作成した解決策です:
resolve_dir() {
(builtin cd `dirname "${1/#~/$HOME}"`'/'`basename "${1/#~/$HOME}"` 2>/dev/null; if [ $? -eq 0 ]; then pwd; fi)
}
これにより、$ 1の絶対パスが解決され、〜でうまく機能し、シンボリックリンクが存在するパスに維持されます。ディレクトリスタックが混乱することはありません。存在しない場合は、フルパスを返すか、何も返しません。$ 1がディレクトリであることを想定しており、そうでない場合はおそらく失敗しますが、これは簡単に確認できます。
おしゃべりで、少し遅い答え。私は古いRHEL4 / 5に行き詰まっているので、それを書く必要があります。絶対リンクと相対リンクを処理し、//、/。/、somedir /../エントリを簡略化します。
test -x /usr/bin/readlink || readlink () {
echo $(/bin/ls -l $1 | /bin/cut -d'>' -f 2)
}
test -x /usr/bin/realpath || realpath () {
local PATH=/bin:/usr/bin
local inputpath=$1
local changemade=1
while [ $changemade -ne 0 ]
do
changemade=0
local realpath=""
local token=
for token in ${inputpath//\// }
do
case $token in
""|".") # noop
;;
"..") # up one directory
changemade=1
realpath=$(dirname $realpath)
;;
*)
if [ -h $realpath/$token ]
then
changemade=1
target=`readlink $realpath/$token`
if [ "${target:0:1}" = '/' ]
then
realpath=$target
else
realpath="$realpath/$target"
fi
else
realpath="$realpath/$token"
fi
;;
esac
done
inputpath=$realpath
done
echo $realpath
}
mkdir -p /tmp/bar
(cd /tmp ; ln -s /tmp/bar foo; ln -s ../.././usr /tmp/bar/link2usr)
echo `realpath /tmp/foo`
GitHubに無料で自由に使用できるように配置した新しいBashライブラリ製品realpath-libをお試しください。完全に文書化されており、優れた学習ツールになります。
ローカル、相対、絶対パスを解決し、Bash 4+以外の依存関係はありません。だから、それはどこでも動作するはずです。それは無料で、クリーンで、シンプルで有益です。
できるよ:
get_realpath <absolute|relative|symlink|local file path>
この関数はライブラリのコアです:
function get_realpath() {
if [[ -f "$1" ]]
then
# file *must* exist
if cd "$(echo "${1%/*}")" &>/dev/null
then
# file *may* not be local
# exception is ./file.ext
# try 'cd .; cd -;' *works!*
local tmppwd="$PWD"
cd - &>/dev/null
else
# file *must* be local
local tmppwd="$PWD"
fi
else
# file *cannot* exist
return 1 # failure
fi
# reassemble realpath
echo "$tmppwd"/"${1##*/}"
return 0 # success
}
また、get_dirname、get_filename、get_stemname、およびvalidate_pathへの関数も含まれています。プラットフォーム間で試してみて、改善にご協力ください。
の問題realpath
は、BSD(またはOSX)では利用できないことです。Linux Journalのかなり古い(2009)記事から抜粋した簡単なレシピは次のとおりです。これは非常に移植性があります。
function normpath() {
# Remove all /./ sequences.
local path=${1//\/.\//\/}
# Remove dir/.. sequences.
while [[ $path =~ ([^/][^/]*/\.\./) ]]; do
path=${path/${BASH_REMATCH[0]}/}
done
echo $path
}
このバリアントもパスが存在する必要がないことに注意してください。
@Andreの回答に基づいて、誰かがループフリーの完全に文字列操作ベースのソリューションを使用している場合に備えて、少し良いバージョンがあるかもしれません。また、realpath
またはを使用することのマイナス面であるシンボリックリンクを逆参照したくない場合にも役立ちreadlink -f
ます。
bashバージョン3.2.25以降で動作します。
shopt -s extglob
normalise_path() {
local path="$1"
# get rid of /../ example: /one/../two to /two
path="${path//\/*([!\/])\/\.\./}"
# get rid of /./ and //* example: /one/.///two to /one/two
path="${path//@(\/\.\/|\/+(\/))//}"
# remove the last '/.'
echo "${path%%/.}"
}
$ normalise_path /home/codemedic/../codemedic////.config
/home/codemedic/.config
hello/../world
(2)ファイル名のドット/hello/..world
(3)ダブルスラッシュの/hello//../world
後のドット(4)ダブルスラッシュの前または後のドット /hello//./world
または /hello/.//world
(5)現在の後の親:/hello/./../world/
(6)親親の後: /hello/../../world
、など- これらの一部は、パスを変更しなくなるまでループを使用して修正することで修正できます。(また削除dir/../
、ではない /dir/..
が、除去dir/..
端部から)
loveborgの優れたpythonスニペットに基づいて、私はこれを書きました:
#!/bin/sh
# Version of readlink that follows links to the end; good for Mac OS X
for file in "$@"; do
while [ -h "$file" ]; do
l=`readlink $file`
case "$l" in
/*) file="$l";;
*) file=`dirname "$file"`/"$l"
esac
done
#echo $file
python -c "import os,sys; print os.path.abspath(sys.argv[1])" "$file"
done
FILEPATH="file.txt"
echo $(realpath $(dirname $FILEPATH))/$(basename $FILEPATH)
これは、ファイルが存在しない場合でも機能します。ファイルを含むディレクトリが存在する必要があります。
realpath -e
3つすべてを実行するソリューションが必要でした。
realpath
そしてreadlink -f
アドオンです#1と#2の両方を含む回答はありませんでした。#3を追加して、他のヤクの毛刈りを保存します。
#!/bin/bash
P="${1?Specify a file path}"
[ -e "$P" ] || { echo "File does not exist: $P"; exit 1; }
while [ -h "$P" ] ; do
ls="$(ls -ld "$P")"
link="$(expr "$ls" : '.*-> \(.*\)$')"
expr "$link" : '/.*' > /dev/null &&
P="$link" ||
P="$(dirname "$P")/$link"
done
echo "$(cd "$(dirname "$P")"; pwd)/$(basename "$P")"
引用を完全に実行するために、パスにねじれたスペースがある短いテストケースを次に示します。
mkdir -p "/tmp/test/ first path "
mkdir -p "/tmp/test/ second path "
echo "hello" > "/tmp/test/ first path / red .txt "
ln -s "/tmp/test/ first path / red .txt " "/tmp/test/ second path / green .txt "
cd "/tmp/test/ second path "
fullpath " green .txt "
cat " green .txt "
私はこれが古代の問題であることを知っています。私はまだ代替案を提供しています。最近、同じ問題に遭遇しましたが、それを行うための既存の移植可能なコマンドは見つかりませんでした。だから私はトリックを行うことができる関数を含む次のシェルスクリプトを書きました。
#! /bin/sh
function normalize {
local rc=0
local ret
if [ $# -gt 0 ] ; then
# invalid
if [ "x`echo $1 | grep -E '^/\.\.'`" != "x" ] ; then
echo $1
return -1
fi
# convert to absolute path
if [ "x`echo $1 | grep -E '^\/'`" == "x" ] ; then
normalize "`pwd`/$1"
return $?
fi
ret=`echo $1 | sed 's;/\.\($\|/\);/;g' | sed 's;/[^/]*[^/.]\+[^/]*/\.\.\($\|/\);/;g'`
else
read line
normalize "$line"
return $?
fi
if [ "x`echo $ret | grep -E '/\.\.?(/|$)'`" != "x" ] ; then
ret=`normalize "$ret"`
rc=$?
fi
echo "$ret"
return $rc
}
https://gist.github.com/bestofsong/8830bdf3e5eb9461d27313c3c282868c
これを処理するための組み込み専用関数を作成し、可能な限り最高のパフォーマンスに焦点を合わせました(楽しみのために)。シンボリックリンクを解決しないため、基本的にはと同じrealpath -sm
です。
## A bash-only mimic of `realpath -sm`.
## Give it path[s] as argument[s] and it will convert them to clean absolute paths
abspath () {
${*+false} && { >&2 echo $FUNCNAME: missing operand; return 1; };
local c s p IFS='/'; ## path chunk, absolute path, input path, IFS for splitting paths into chunks
local -i r=0; ## return value
for p in "$@"; do
case "$p" in ## Check for leading backslashes, identify relative/absolute path
'') ((r|=1)); continue;;
//[!/]*) >&2 echo "paths =~ ^//[^/]* are impl-defined; not my problem"; ((r|=2)); continue;;
/*) ;;
*) p="$PWD/$p";; ## Prepend the current directory to form an absolute path
esac
s='';
for c in $p; do ## Let IFS split the path at '/'s
case $c in ### NOTE: IFS is '/'; so no quotes needed here
''|.) ;; ## Skip duplicate '/'s and '/./'s
..) s="${s%/*}";; ## Trim the previous addition to the absolute path string
*) s+=/$c;; ### NOTE: No quotes here intentionally. They make no difference, it seems
esac;
done;
echo "${s:-/}"; ## If xpg_echo is set, use `echo -E` or `printf $'%s\n'` instead
done
return $r;
}
注:パスの先頭//
にある2つの二重スラッシュは実装定義の動作であるため、この関数はで始まるパスを処理しません。ただし、、、/
などは問題なく処理さ///
れます。
この関数はすべてのエッジケースを適切に処理するようですが、まだ扱っていないものがあるかもしれません。
パフォーマンスに関する注意:何千もの引数を指定して呼び出すと、abspath
実行速度がの約10倍になりrealpath -sm
ます。単一の引数で呼び出されたabspath
場合realpath -sm
、私のマシンよりも110倍以上速く実行されます。これは、ほとんどの場合、毎回新しいプログラムを実行する必要がないためです。
今日、stat
コマンドを使用してパスを解決できることがわかりました。
したがって、「〜/ Documents」のようなディレクトリの場合:
これを実行できます:
stat -f %N ~/Documents
完全なパスを取得するには:
/Users/me/Documents
シンボリックリンクの場合、%Y形式オプションを使用できます。
stat -f %Y example_symlink
次のような結果を返す可能性があります:
/usr/local/sbin/example_symlink
* NIXの他のバージョンでは書式設定オプションが異なる場合がありますが、これらはOSXで動作しました。
stat -f %N ~/Documents
ラインが赤ニシンです...あなたのシェルが交換され~/Documents
て/Users/me/Documents
、そしてstat
ちょうどその引数の逐語的に印刷しています。
/foo/bar
か/foo
、またはパス名のルールに従って文字列操作の側面のみに関心があるかどうかは重要ですか?