/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/../../
正規化するためにパスが存在する必要はありません。
これを行うための直接bashコマンドがあるかどうかはわかりませんが、通常は
normalDir="`cd "${dirToNormalize}";pwd`"
echo "${normalDir}"
そしてそれはうまく機能します。
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);
}
ポータブルで信頼性の高いソリューションは、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でエラーが発生します。
古い質問ですが、もっと簡単な方法があります フルパス名を扱っている場合 シェルレベルで:
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
に置き換えるだけです。
私の最近の解決策は:
pushd foo/bar/..
dir=`pwd`
popd
ティムウィットコムの回答に基づきます。
正確な答えではなく、おそらくフォローアップの質問(元の質問は明示的ではありませんでした):
readlink
は、実際にシンボリックリンクを追跡する場合は問題ありません。しかし、単に./
シーケンスと../
シーケンスと//
シーケンスを正規化するユースケースもあります。これは、純粋に構文的に行うことができ、withoutシンボリックリンクを正規化します。 readlink
はこれに適しておらず、realpath
も同様です。
for f in $paths; do (cd $f; pwd); done
既存のパスでは機能しますが、他のパスでは機能しません。
sed
スクリプトは、Perlのようなものを使用せずにシーケンス(/foo/bar/baz/../..
-> /foo/bar/..
-> /foo
)を繰り返し置き換えることができないことを除いて、良い方法のようです。すべてのシステムで想定したり、someいループを使用して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
おしゃべり、そして少し遅い答え。古いRHEL4/5にこだわっているので、1つ書く必要があります。絶対リンクと相対リンクを処理し、//、/。/、および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`
私はパーティーに遅れていますが、これはこのようなスレッドの束を読んだ後、私が作成したソリューションです:
resolve_dir() {
(builtin cd `dirname "${1/#~/$HOME}"`'/'`basename "${1/#~/$HOME}"` 2>/dev/null; if [ $? -eq 0 ]; then pwd; fi)
}
これにより、$ 1の絶対パスが解決され、〜でNiceが再生され、シンボリックリンクが存在するパスに保持され、ディレクトリスタックが混乱することはありません。完全なパスを返すか、存在しない場合は何も返しません。 $ 1がディレクトリであると想定し、そうでない場合はおそらく失敗しますが、それは簡単に確認できます。
新しいBashライブラリ製品 realpath-lib を試してください。これは、GitHubに無料で邪魔にならないように配置したものです。完全に文書化されており、優れた学習ツールになります。
ローカル、相対、絶対パスを解決し、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の関数も含まれています。プラットフォーム間で試してみて、改善に役立ててください。
@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
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
}
このバリアントは、パスが存在することをnotも必要とすることに注意してください。
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 "
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)
これは、ファイルが存在しない場合でも機能します。ファイルを含むディレクトリが存在する必要があります。
これは古代の質問です。私はまだ代替手段を提供しています。最近、私は同じ問題に出会い、それを行うための既存の移植可能なコマンドが見つからなかった。そこで、このトリックを実行できる関数を含む次のシェルスクリプトを作成しました。
#! /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