我有一个存储在 shell 变量中的路径列表,tmp
例如:
/abc/bcd/def/ZRT834/ZRT834_9/5678/S1_L001_R1.tar
/abc/bcd/def/ZRT834/ZRT834_9/5678/S2_L001_I1.tar
/abc/bcd/def/ZRT834/ZRT834_9/5678/S1_L001_I2.tar
/abc/bcd/def/ZRT207/ZRT207_1/5678/S1_L001_R1.tar
/abc/bcd/def/ZRT207/ZRT207_1/5678/S1_L001_R2.tar
/abc/bcd/def/ZRT207/ZRT207_1/5678/S1_L001_I2.tar
我想根据路径中的匹配模式创建新目录。在上面的示例中,我想创建目录ZRT834_9
并ZRT207_1
为tar
文件创建软链接到其相应的目录中。
我的输出应该类似于:ZRT834_9
目录包含S1_L001_R1.tar
、S2_L001_I1.tar
和S1_L001_I2.tar
我该如何实现这一目标?
答案1
在zsh
:
files=(
/abc/bcd/def/ZRT834/ZRT834_9/5678/S1_L001_R1.tar
/abc/bcd/def/ZRT834/ZRT834_9/5678/S2_L001_I1.tar
/abc/bcd/def/ZRT834/ZRT834_9/5678/S1_L001_I2.tar
/abc/bcd/def/ZRT207/ZRT207_1/5678/S1_L001_R1.tar
/abc/bcd/def/ZRT207/ZRT207_1/5678/S1_L001_R2.tar
/abc/bcd/def/ZRT207/ZRT207_1/5678/S1_L001_I2.tar
)
# or files=(${(f)"$(<list.txt)"}) to get the file list from the non-empty
# lines of list.txt, or files=($=tmp) for word splitting the contents
# of a $tmp scalar variable according to the current value of $IFS.
for file ($files) {
dir=${file:h5:t}
mkdir -p -- $dir && ln -s -- $file $dir/
}
从哪里${file:h5}
获取 的5
-component1 的h
头部$file
和:t
结果的尾部。或者你也可以${file:t3:h1}
从最后开始数。
¹ 这里成分是路径组件。例如,在 中,../a//b///c/./d/e
组件将为..
、a
、b
、c
、.
和d
。会给予,然后会屈服。另请参阅在应用和之前获取规范的绝对路径。e
:h5
../a//b///c/.
:t
.
${file:A:h5:t}
:h5
:t
答案2
根据您的描述,以下假设:
- 变量
tmp
包含换行符分隔的文件名列表[1] - 你想提取第五文件名中的路径元素(例如
ZRT834_9
和ZRT207_1
) - 您想要使用该路径元素创建一个子目录(如果该子目录尚不存在)
- 您想要将文件名符号链接到新创建的目录中。
#!/bin/bash
tmp="/abc/bcd/def/ZRT834/ZRT834_9/5678/S1_L001_R1.tar
/abc/bcd/def/ZRT834/ZRT834_9/5678/S2_L001_I1.tar
/abc/bcd/def/ZRT834/ZRT834_9/5678/S1_L001_I2.tar
/abc/bcd/def/ZRT207/ZRT207_1/5678/S1_L001_R1.tar
/abc/bcd/def/ZRT207/ZRT207_1/5678/S1_L001_R2.tar
/abc/bcd/def/ZRT207/ZRT207_1/5678/S1_L001_I2.tar"
while read -r f ; do
d="$(echo "$f" | sed -E 's:^(/+[^/]+){4}/+([^/]*)/.*:\2:')"
[ -z "$d" ] && echo "Error: no fifth element in path: '$f'" && exit 1
mkdir -p "$d" || exit 1
ln -s "$f" "$d/"
done <<< "$tmp"
该sed
脚本使用扩展正则表达式(选项)并且(大致翻译为英语)将(一个或多个斜杠后跟一个或多个非斜杠字符)的-E
前 4 组( )捕获到捕获组 1 中,然后一个或多个斜杠之后的下一个斜线进入捕获组二,并将整个输入行替换为捕获组二 ( ) 。{4}
/+[^/]+
[^/]+
\2
“一个或多个斜杠”是因为像这样的路径名是完全有效的/foo/////////////////bar////baz
- 多余的 /s 将被忽略。顺便说一句,某些程序(例如smbclient
)将解释路径名的第一个元素开始使用 2 个斜杠作为服务器名称前缀,但大多数程序并非如此。
[1] 你确实应该为此使用数组。例如
#!/bin/bash
# double-quote each array element even though your sample
# data doesn't need to be quoted - because other filenames
# might contain white-space or shell metacharacters.
tmp=("/abc/bcd/def/ZRT834/ZRT834_9/5678/S1_L001_R1.tar"
"/abc/bcd/def/ZRT834/ZRT834_9/5678/S2_L001_I1.tar"
"/abc/bcd/def/ZRT834/ZRT834_9/5678/S1_L001_I2.tar"
"/abc/bcd/def/ZRT207/ZRT207_1/5678/S1_L001_R1.tar"
"/abc/bcd/def/ZRT207/ZRT207_1/5678/S1_L001_R2.tar"
"/abc/bcd/def/ZRT207/ZRT207_1/5678/S1_L001_I2.tar")
for f in "${tmp[@]}" ; do
d="$(echo "$f" | sed -E 's:^(/+[^/]+){4}/+([^/]*)/.*:\2:')"
[ -z "$d" ] && echo "Error: no fifth element in path: '$f'" && exit 1
mkdir -p "$d" || exit 1
ln -s "$f" "$d/"
done
答案3
GNU sed 能够使用替换结构中的反向链接参数执行 shell 命令:
sed 's%.*/\([^/]*/\)[^/]*/[^/]*%mkdir -p "\1";ln -s "&" "\1"%e;d' <<<"$tmp"