bash:提取路径名的最后两个目录
声明:本页面是StackOverFlow热门问题的中英对照翻译,遵循CC BY-SA 4.0协议,如果您需要使用它,必须同样遵循CC BY-SA许可,注明原文地址和作者信息,同时你必须将它归于原作者(不是我):StackOverFlow
原文地址: http://stackoverflow.com/questions/8223170/
Warning: these are provided under cc-by-sa 4.0 license. You are free to use/share it, But you must attribute it to the original authors (not me):
StackOverFlow
bash: extracting last two dirs for a pathname
提问by user1059256
I seem to have failed at something pretty simple, in bash. I have a string variable that holds the full path to a directory. I'd like to assign the last twodirectories in it to another string. For example, if I have:
在 bash 中,我似乎在一些非常简单的事情上失败了。我有一个字符串变量,用于保存目录的完整路径。我想将其中的最后两个目录分配给另一个字符串。例如,如果我有:
DIRNAME = /a/b/c/d/e
I'd like:
我想要:
DIRNAME2 = d/e
I'm sure there's a simple bash construct or sedcommand that will do it, but it's escaping me. I'd sort of like a generalized version of basenameor dirnamethat doesn't just return the extreme parts of a name.
我确信有一个简单的 bash 构造或sed命令可以做到这一点,但它正在逃避我。我有点像basenameor的广义版本dirname,它不仅仅返回名称的极端部分。
Thanks! Dave
谢谢!戴夫
采纳答案by Jonathan Leffler
DIRNAME="/a/b/c/d/e"
D2=$(dirname "$DIRNAME")
DIRNAME2=$(basename "$D2")/$(basename "$DIRNAME")
Or, in one line (but be careful with all the double quotes — it is easier when it is split up):
或者,在一行中(但要小心所有的双引号——拆分起来更容易):
DIRNAME2=$(basename "$(dirname "$DIRNAME")")/$(basename "$DIRNAME")
Don't try that game with back-quotes unless you're heavily into masochism. And if there might be spaces in the paths, use double quotes around the variable names.
除非您严重受虐,否则不要尝试使用反引号进行游戏。如果路径中可能有空格,请在变量名称周围使用双引号。
This will work with almost any shell Korn Shell as well as Bash. In bash, there are other mechanisms available - other answers illustrate some of the many options, though expris also an old-school solution (it was present in 7th Edition Unix too). This code using back-quotes works in Bash and Korn shell too — but not in Heirloom Shell (which is similar to a Unix System V Release 2/3/4 shell, IIRC).
这将适用于几乎所有 shell Korn Shell 以及 Bash。在 中bash,还有其他可用的机制 - 其他答案说明了许多选项中的一些,尽管expr这也是一个老式的解决方案(它也出现在第 7 版 Unix 中)。这段使用反引号的代码也适用于 Bash 和 Korn shell,但不适用于 Heirloom Shell(类似于 Unix System V Release 2/3/4 shell,IIRC)。
DIRNAME2=`basename "\`dirname \"$DIRNAME\"\`"`/`basename "$DIRNAME"`
(Two levels of nesting is not too awful, but it is pretty bad; three gets really tricky!)
(两层嵌套不算太糟糕,但很糟糕;三层真的很棘手!)
Testing
测试
When testing path name manipulation that should survive spaces in the path name, it is worth testing with a name containing double-spaces (rather than, or as well as, single spaces). For example:
当测试路径名操作应该保留路径名中的空格时,值得使用包含双空格(而不是单空格)的名称进行测试。例如:
DIRNAME="/a b/ c d / ee ff / gg hh "
echo "DIRNAME=[[$DIRNAME]]"
echo "basename1=[[$(basename "$DIRNAME")]]"
echo "basename2=[[`basename \"$DIRNAME\"`]]"
echo
D2=$(dirname "$DIRNAME")
echo "D2=[[$D2]]"
DIRNAME2=$(basename "$D2")/$(basename "$DIRNAME")
echo "DIRNAME2=[[$DIRNAME2]]"
echo
DIRNAME3=$(basename "$(dirname "$DIRNAME")")/$(basename "$DIRNAME")
echo "DIRNAME3=[[$DIRNAME3]]"
DIRNAME4=`basename "\`dirname \"$DIRNAME\"\`"`/`basename "$DIRNAME"`
echo "DIRNAME4=[[$DIRNAME2]]"
The output from that is:
输出是:
DIRNAME=[[/a b/ c d / ee ff / gg hh ]]
basename1=[[ gg hh ]]
basename2=[[ gg hh ]]
D2=[[/a b/ c d / ee ff ]]
DIRNAME2=[[ ee ff / gg hh ]]
DIRNAME3=[[ ee ff / gg hh ]]
DIRNAME4=[[ ee ff / gg hh ]]
回答by Xiè Jìléi
I prefer to use the builtins as much as I can, to avoid create unnecessary processes. Because your script may be run under Cygwin or other OS whose process creation are very expensive.
我更喜欢尽可能多地使用内置函数,以避免创建不必要的进程。因为您的脚本可能在 Cygwin 或其他进程创建非常昂贵的操作系统下运行。
I think it's not so lengthy if you just want to extract two dirs:
如果您只想提取两个目录,我认为它不会那么长:
base1="${DIRNAME##*/}"
dir1="${DIRNAME%/*}"
DIRNAME2="${dir1##*/}/$base1"
This can also avoid special char problems involved in executing another commands.
这也可以避免执行另一个命令时涉及的特殊字符问题。
回答by Gordon Davisson
I don't know of a method specifically for trimming paths, but you can certainly do it with bash's regular expression matching:
我不知道专门用于修剪路径的方法,但您当然可以使用bash 的正则表达式匹配来做到这一点:
DIRNAME=/a/b/c/d/e
if [[ "$DIRNAME" =~ ([^/]+/+[^/]+)/*$ ]]; then
echo "Last two: ${BASH_REMATCH[1]}"
else
echo "No match"
fi
Note: I've made the pattern here a little more complex than you might expect, in order to handle some allowed-but-not-common things in the path: it trims trailing slashes, and tolerates multiple (redundant) slashes between the last two names. For example, running it on "/a/b/c//d//" will match "c//d".
注意:为了处理路径中一些允许但不常见的事情,我在此处制作的模式比您预期的要复杂一些:它修剪尾部斜杠,并在最后一个斜杠之间容忍多个(冗余)斜杠两个名字。例如,在“/a/b/c//d//”上运行它会匹配“c//d”。
回答by coelmay
DIRNAME="a/b/c/d/e"
DIRNAME2=`echo $DIRNAME | awk -F/ '{print $(NF-1)"_"$(NF)}'`
DIRNAME2 then has the value d_e
Change the underscore to whatever you wish.
将下划线更改为您想要的任何内容。
回答by harpo
I think there's a shorter way using globs, but:
我认为使用 glob 有一种更短的方法,但是:
$ DIRNAME='a/b/c/d/e'
$ LAST_TWO=$(expr "$DIRNAME" : '.*/\([^/]*/[^/]*\)$')
$ echo $LAST_TWO
d/e
回答by Chris F.A. Johnson
dirname=/a/b/c/d/e
IFS=/ read -a dirs <<< "$dirname"
printf "%s/%s\n" "${dirs[-2]}" "${dirs[-1]}"
回答by Priyank Bhatnagar
function getDir() {
echo | awk -F/ '
{
n=NF-''+1;
if(n<1)exit;
for (i=n;i<=NF;i++) {
printf("/%s",$i);
}
}'
}
dir="/a/b/c/d/e"
dir2=`getDir $dir 1`
echo $dir2
You can get any number of directories from the last from this function. For your case run it as,
你可以从这个函数的最后一个目录中获取任意数量的目录。对于你的情况,运行它,
dir2=`getDir $dir 2`;
Output : /d/e
输出:/d/e

