根据模式提取子目录名称

根据模式提取子目录名称

我有一个存储在 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_9ZRT207_1tar文件创建软链接到其相应的目录中。

我的输出应该类似于:ZRT834_9目录包含S1_L001_R1.tarS2_L001_I1.tarS1_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组件将为..abc.d。会给予,然后会屈服。另请参阅在应用和之前获取规范的绝对路径。e:h5../a//b///c/.:t.${file:A:h5:t}:h5:t

答案2

根据您的描述,以下假设:

  • 变量tmp包含换行符分隔的文件名列表[1]
  • 你想提取第五文件名中的路径元素(例如ZRT834_9ZRT207_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"

相关内容