bash shell脚本以查找几个文件的最近父目录
假设输入参数是多个文件的完整路径。说bash shell脚本以查找几个文件的最近父目录,bash,Bash,假设输入参数是多个文件的完整路径。说 /abc/def/file1 /abc/def/ghi/file2 /abc/def/ghi/file3 如何在bashshell脚本中获取目录名/abc/def 如何仅获取file1、/ghi/file2和/ghi/file3 要获取父目录,请执行以下操作: dirname /abc/def/file1 将给出/abc/def 和获取文件名 basename /abc/def/file1 将给出文件1 根据您的问题,仅获取最近的父目录名称使
/abc/def/file1
/abc/def/ghi/file2
/abc/def/ghi/file3
/abc/def
file1
、/ghi/file2
和/ghi/file3
要获取父目录,请执行以下操作:
dirname /abc/def/file1
将给出/abc/def
和获取文件名
basename /abc/def/file1
将给出文件1
根据您的问题,仅获取最近的父目录名称使用
basename $(dirname $(/abc/def/file1))
将提供def
在此处输入代码给定第1部分的答案(通用前缀),第2部分的答案是直截了当的;您可以将每个名称的前缀切掉,这可以通过
sed
和其他选项来完成
那么,有趣的部分是找到公共前缀。最小公共前缀是/
(例如,对于/etc/passwd
和/bin/sh
)。最大公共前缀(根据定义)存在于所有字符串中,因此我们只需将其中一个字符串拆分为段,并将可能的前缀与其他字符串进行比较。概述:
split name A into components
known_prefix="/"
for each extra component from A
do
possible_prefix="$known_prefix/$extra/"
for each name
do
if $possible_prefix is not a prefix of $name
then ...all done...break outer loop...
fi
done
...got here...possible prefix is a prefix!
known_prefix=$possible_prefix
done
有一些管理细节需要处理,例如名称中的空格。还有,什么是允许的武器。问题被标记为bash
,但允许使用哪些外部命令(例如Perl)
一个未定义的问题-假设名称列表为:
/abc/def/ghi
/abc/def/ghi/jkl
/abc/def/ghi/mno
最长的公共前缀是/abc/def
还是/abc/def/ghi
?我假设这里最长的公共前缀是/abc/def
。(如果您确实希望它是/abc/def/ghi
,则使用/abc/def/ghi/
作为第一个名称。)
此外,还有调用详细信息:
- 如何调用此函数或命令
- 如何返回值
- 这是一个还是两个函数或命令(
和“path\u without\u prefix”)longest\u common\u prefix
prefix=$(最长的\u公共\u前缀name1[name2…])
suffix=$(路径\不带\前缀/pre/fix/pre/fix/to/file[…])
path\u without\u prefix
命令将删除该前缀,如果前缀没有开始名称,则参数保持不变
最长公共前缀
测试:
输出:
Test: /abc/def/file 0 /abc/def/file1 /abc/def/ghi/file2 /abc/def/ghi/file3 /abc/def/ghi/file 4
/abc/def
Test: /abc/def/file 0 /abc/def/file1 /abc/def/ghi/file2 /abc/def/ghi/file3 /abc/def/ghi/file 4 abc/def
Test: /abc/def/ghi/jkl /abc/def/ghi /abc/def/ghi/mno
/abc/def
Test: /abc/def/file1 /abc/def/ghi/file2 /abc/def/ghi/file3
/abc/def
Test: /a c/d f/file1 /a c/d f/ghi/file2 /a c/d f/ghi/file3
/a c/d f
abc
def/ghi
/usr/bin/sh
不带前缀的路径
测试:
输出:
Test: /abc/def/file 0 /abc/def/file1 /abc/def/ghi/file2 /abc/def/ghi/file3 /abc/def/ghi/file 4
/abc/def
Test: /abc/def/file 0 /abc/def/file1 /abc/def/ghi/file2 /abc/def/ghi/file3 /abc/def/ghi/file 4 abc/def
Test: /abc/def/ghi/jkl /abc/def/ghi /abc/def/ghi/mno
/abc/def
Test: /abc/def/file1 /abc/def/ghi/file2 /abc/def/ghi/file3
/abc/def
Test: /a c/d f/file1 /a c/d f/ghi/file2 /a c/d f/ghi/file3
/a c/d f
abc
def/ghi
/usr/bin/sh
一个更“可移植”的解决方案,从某种意义上说,它不使用特定于bash的特性:首先定义一个函数来计算两条路径的最长公共前缀:
function common_path()
{
lhs=$1
rhs=$2
path=
OLD_IFS=$IFS; IFS=/
for w in $rhs; do
test "$path" = / && try="/$w" || try="$path/$w"
case $lhs in
$try*) ;;
*) break ;;
esac
path=$try
done
IFS=$OLD_IFS
echo $path
}
然后用它列出一长串单词:
function common_path_all()
{
local sofar=$1
shift
for arg
do
sofar=$(common_path "$sofar" "$arg")
done
echo ${sofar:-/}
}
有了你的投入,它就会
$ common_path_all /abc/def/file1 /abc/def/ghi/file2 /abc/def/ghi/file3
/abc/def
正如Jonathan Leffler所指出的,一旦你有了这个问题,第二个问题就无关紧要了。这已经被证明是可以使用的(包括换行符、退格等):
在我看来,下面的解决方案要简单得多 如前所述,只有第1部分比较棘手。第2部分简单介绍了sed 第1部分可分为2个子部分:
LONGEST_PREFIX=$(printf "%s\n%s\n" "$file_1" "$file_2" | sed -e 'N;s/^\(.*\).*\n\1.*$/\1/')
CLOSEST_PARENT=$(echo "$LONGEST_PREFIX" | sed 's/\(.*\)\/.*/\1/')
这当然可以在一行中重写:
CLOSEST_PARENT=$(printf "%s\n%s\n" "$file_1" "$file_2" | sed -e 'N;s/^\(.*\).*\n\1.*$/\1/' | sed 's/\(.*\)\/.*/\1/')
请注意,此解决方案假定路径名是绝对的。如果路径名是相对的(即
/abc/def/file1
和/abc/def/file2/
),最长的公共前缀将以无限循环失败。通过将条件[“$x”!=“/”]
更改为[“$x”!=“/”-a“$x”!=”]
,可以轻松修复此问题以适应相对路径。
path_common() {
if [ $# -ne 2 ]
then
return 2
fi
# Remove repeated slashes
for param
do
param="$(printf %s. "$1" | tr -s "/")"
set -- "$@" "${param%.}"
shift
done
common_path="$1"
shift
for param
do
while case "${param%/}/" in "${common_path%/}/"*) false;; esac; do
new_common_path="${common_path%/*}"
if [ "$new_common_path" = "$common_path" ]
then
return 1 # Dead end
fi
common_path="$new_common_path"
done
done
printf %s "$common_path"
}
LONGEST_PREFIX=$(printf "%s\n%s\n" "$file_1" "$file_2" | sed -e 'N;s/^\(.*\).*\n\1.*$/\1/')
CLOSEST_PARENT=$(echo "$LONGEST_PREFIX" | sed 's/\(.*\)\/.*/\1/')
CLOSEST_PARENT=$(printf "%s\n%s\n" "$file_1" "$file_2" | sed -e 'N;s/^\(.*\).*\n\1.*$/\1/' | sed 's/\(.*\)\/.*/\1/')