假设输入参数是多个文件的FULL路径。说,
/abc/def/file1
/abc/def/ghi/file2
/abc/def/ghi/file3
/abc/def
?file1
,/ghi/file2
和/ghi/file3
?答案 0 :(得分:4)
鉴于第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/.
作为第一个名称。)
此外,还有调用详细信息:
longest_common_prefix
和'path_without_prefix`)?两个命令更容易:
prefix=$(longest_common_prefix name1 [name2 ...])
suffix=$(path_without_prefix /pre/fix /pre/fix/to/file [...])
path_without_prefix
命令删除前缀(如果存在),如果前缀未启动名称,则保留参数不变。
longest_common_prefix()
{
declare -a names
declare -a parts
declare i=0
names=("$@")
name="$1"
while x=$(dirname "$name"); [ "$x" != "/" ]
do
parts[$i]="$x"
i=$(($i + 1))
name="$x"
done
for prefix in "${parts[@]}" /
do
for name in "${names[@]}"
do
if [ "${name#$prefix/}" = "${name}" ]
then continue 2
fi
done
echo "$prefix"
break
done
}
测试:
set -- "/abc/def/file 0" /abc/def/file1 /abc/def/ghi/file2 /abc/def/ghi/file3 "/abc/def/ghi/file 4"
echo "Test: $@"
longest_common_prefix "$@"
echo "Test: $@" abc/def
longest_common_prefix "$@" abc/def
set -- /abc/def/ghi/jkl /abc/def/ghi /abc/def/ghi/mno
echo "Test: $@"
longest_common_prefix "$@"
set -- /abc/def/file1 /abc/def/ghi/file2 /abc/def/ghi/file3
echo "Test: $@"
longest_common_prefix "$@"
set -- "/a c/d f/file1" "/a c/d f/ghi/file2" "/a c/d f/ghi/file3"
echo "Test: $@"
longest_common_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
path_without_prefix()
{
local prefix="$1/"
shift
local arg
for arg in "$@"
do
echo "${arg#$prefix}"
done
}
测试:
for name in /pre/fix/abc /pre/fix/def/ghi /usr/bin/sh
do
path_without_prefix /pre/fix $name
done
输出:
abc
def/ghi
/usr/bin/sh
答案 1 :(得分:2)
更“便携”的解决方案,因为它不使用特定于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指出的那样,一旦你有了这个,第二个问题就是微不足道。
答案 2 :(得分:2)
Here's one已被证明与arbitrarily complex file names一起使用(包含换行符,退格等):
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"
}
答案 3 :(得分:1)
在我看来,下面的解决方案要简单得多。
如前所述,只有第1部分很棘手。第2部分很简单,有sed。
第1部分可分为2个子部分:
可以使用以下代码完成。为了清楚起见,此示例仅使用2个字符串,但while循环为您提供了n个字符串所需的内容。
library(splitstackshape)
library(data.table)
cSplit(setDT(df1, keep.rownames=TRUE), 'pages', ',', 'long')[,
list(pages=toString(sort(pages)), sum.num.= sum.num.[1]) ,rn
][,list(Sum=sum(sum.num.)) , .(pages)]
# pages Sum
#1: Badezimmer, Baumarkt, Büromöbel 22
#2: Badezimmer, Baumarkt, Dekoration 14
#3: Badezimmer, Baumarkt, Flur 70
#4: Badezimmer, Baumarkt, Garten 18
#5: Badezimmer, Baumarkt, Heimtextilien 100
#6: Badezimmer, Baumarkt, Kinder 28
当然可以只用一行重写:
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/')
答案 4 :(得分:-2)
获取父母的目录:
dirname /abc/def/file1
将提供 / abc / def
获取文件名
basename /abc/def/file1
将提供 file1
根据您的问题,只使用最近的父目录名称
basename $(dirname $(/abc/def/file1))
将提供 def 在这里输入代码