提问人:blakem 提问时间:11/2/2023 更新时间:11/2/2023 访问量:74
将 bash 变量传递给 grep 命令的文件名
pass bash variable to filename for grep command
问:
我有一个文件,其中包含一个文件名前缀列表,我想对这些前缀执行 grep 以 grep 出一组特定的行,这些行包含坐标,用于后续计算均方根偏差,我将在 awk 中使用单行代码执行。我已经验证了我的正则表达式的语法适用于单个文件,但是当我从 for 循环中使用 bash 变量时,grep 将无法识别该变量。我尝试了双引号、转义字符和波浪形大括号的多种排列,但它们都没有产生所需的输出。以下是初始 grep 后的原始数据的样子,并通过管道到第二个 grep 执行以清理 awk 步骤的数据:
grep " [A-Z] " ../glide-dock_SP_8CHM/ligands/3579831839.sdf | grep " 'F\|S\|C\|O\|N "
2.7118 4.0281 21.0125 S 0 0 0 0 0 0
4.0921 3.8708 21.4967 O 0 0 0 0 0 0
1.8208 2.8602 20.9648 O 0 0 0 0 0 0
1.9954 5.2598 21.8979 N 0 0 1 0 0 0
2.8079 4.6789 19.3978 N 0 0 0 0 0 0
2.3264 6.6613 21.7269 C 0 0 0 0 0 0
0.5679 5.5328 21.8880 C 0 0 0 0 0 0
3.8805 4.1396 18.5518 C 0 0 0 0 0 0
1.5591 4.8798 18.6409 C 0 0 0 0 0 0
0.9197 6.9883 22.3042 C 0 0 0 0 0 0
0.8840 7.1094 23.8539 C 0 0 0 0 0 0
0.1007 8.1332 21.6760 C 0 0 0 0 0 0
-0.1257 6.7465 24.4613 O 0 0 0 0 0 0
1.9559 7.6325 24.4759 N 0 0 0 0 0 0
0.9264 9.3760 21.3740 C 0 0 0 0 0 0
2.1242 7.8085 25.9208 C 0 0 1 0 0 0
1.5512 9.5358 20.1194 C 0 0 0 0 0 0
1.0887 10.3742 22.3570 C 0 0 0 0 0 0
3.4722 7.2469 26.3919 C 0 0 0 0 0 0
1.7964 9.2425 26.3963 C 0 0 0 0 0 0
2.3648 10.6567 19.8673 C 0 0 0 0 0 0
1.8942 11.5002 22.1018 C 0 0 0 0 0 0
3.4888 6.2142 27.0561 O 0 0 0 0 0 0
4.6188 7.8580 26.0568 N 0 0 0 0 0 0
2.6369 10.3760 25.7768 C 0 0 0 0 0 0
2.5397 11.6366 20.8599 C 0 0 0 0 0 0
4.8138 9.1019 25.3276 C 0 0 0 0 0 0
4.1501 10.2988 26.0247 C 0 0 0 0 0 0
我有一个文件列表,其中包含我希望执行此搜索和提取的所有文件的前缀,因此我认为 for 语句将是要走的路。这是我最初的尝试:
for i in $(cat ligand-list.txt)
> do
> grep " [A-Z] " ../glide-dock_SP_8CHM/ligands/$i.sdf | grep " 'F\|S\|C\|O\|N " | awk '{print $1,$2,$3}' > $i_8CHM.xyz
> done
grep: ../glide-dock_SP_8CHM/ligands/ligand-list.txt.sdf: No such file or directory
我知道我需要在 grep 命令中的变量周围进行某种字符转义,所以我想使用双引号,但这产生了与上面相同的输出。添加带有双引号的转义字符会将$i视为文本字符串:
for i in $(cat ligand-list.txt)
> do
> grep " [A-Z] " ../glide-dock_SP_8CHM/ligands/"\$i".sdf | grep " 'F\|S\|C\|O\|N " | awk '{print $1,$2,$3}' > $i_8CHM.xyz
> done
.
.
.
grep: ../glide-dock_SP_8CHM/ligands/$i.sdf: No such file or directory
添加波浪形大括号是我能够得到的最接近的,因为我的 for 变量作为字符串传递给 grep,但 grep 现在将转义字符和波浪形大括号视为 bash 变量中的字符串:
for i in $(cat ligand-list.txt)
> do
> grep " [A-Z] " ../glide-dock_SP_8CHM/ligands/"\{$i}".sdf | grep " 'F\|S\|C\|O\|N " | awk '{print $1,$2,$3}' > $i_8CHM.xyz
> done
.
.
.
grep: ../glide-dock_SP_8CHM/ligands/\{3579831839}.sdf: No such file or directory
,“,{} 的任何其他组合或位置变化都会产生相同的结果。根据我对 SO 和 grep 文档的阅读,我认为我只需要双引号即可将我的 bash 变量作为文字字符串传递,但我认为这只是针对 grep 搜索的正则表达式,而不是 grep 正在搜索的文件。非常感谢对此的澄清,因为我一天中的大部分时间都在用这个敲击键盘。
答:
设置:
$ mkdir -p ../glide-dock_SP_8CHM/ligands
$ cat ligand-list.txt
12345
78900
ABCDE
$ head ../glide-dock_SP_8CHM/ligands/*sdf
==> ../glide-dock_SP_8CHM/ligands/12345.sdf <==
99.9999 99.9999 12345 A 0 0 0 0 0 0
2.7118 4.0281 12345 F 0 0 0 0 0 0
99.9999 99.9999 12345 G 0 0 0 0 0 0
4.0921 3.8708 12345 S 0 0 0 0 0 0
==> ../glide-dock_SP_8CHM/ligands/78900.sdf <==
99.9999 99.9999 78900 B 0 0 0 0 0 0
2.7118 4.0281 78900 C 0 0 0 0 0 0
99.9999 99.9999 78900 M 0 0 0 0 0 0
4.0921 3.8708 78900 O 0 0 0 0 0 0
==> ../glide-dock_SP_8CHM/ligands/ABCDE.sdf <==
99.9999 99.9999 ABCDE L 0 0 0 0 0 0
2.7118 4.0281 ABCDE O 0 0 0 0 0 0
99.9999 99.9999 ABCDE Z 0 0 0 0 0 0
4.0921 3.8708 ABCDE F 0 0 0 0 0 0
笔记:
- 对于我们不感兴趣的行,第 1/2 列始终为 99.9999
- 对于感兴趣的行,第 3 列更改为文件名(更容易查看结果)
修改 OP 的代码:bash / while
while read -r i
do
grep '[[:upper:]]' ../glide-dock_SP_8CHM/ligands/"$i".sdf | grep " [FSCON] " | awk '{print $1,$2,$3}' > "$i"_8CHM.xyz
done < ligand-list.txt
合并 2 倍呼叫:grep
while read -r i
do
grep " [FSCON] " ../glide-dock_SP_8CHM/ligands/"$i".sdf | awk '{print $1,$2,$3}' > "$i"_8CHM.xyz
done < ligand-list.txt
删除通话:grep
while read -r i
do
awk '$4 ~ /^[FSCON]$/ {print $1,$2,$3}' ../glide-dock_SP_8CHM/ligands/"$i".sdf > "$i"_8CHM.xyz
done < ligand-list.txt
使用循环构建文件列表,然后使用单个脚本来处理所述文件列表:while
awk
file_list=()
while read -r line
do
file_list+=("../glide-dock_SP_8CHM/ligands/${line}.sdf")
done < ligand-list.txt
awk '
FNR==1 { close(outf) # close previous output file
n = split(FILENAME,a,/[/.]/) # split current FILENAME on dual characters "/" and "."
outf = a[n-1] "_8CHM.xyz" # create output filename from (n-1)st entry from array a[]
}
$4 ~ /^[FSCON]$/ { print $1, $2, $3 > outf } # print desired columns to output file
' "${file_list[@]}"
笔记:
- 所有这些解决方案都假定每个条目都存在一个文件,否则我们将生成错误
ligand-list.txt
'No such file or directory'
- 如果文件可能不存在,则可以更新每个解决方案以包含一个测试,以在尝试处理所述文件之前验证文件是否存在
这些都会产生:
$ head *_8CHM.xyz
==> 12345_8CHM.xyz <==
2.7118 4.0281 12345
4.0921 3.8708 12345
==> 78900_8CHM.xyz <==
2.7118 4.0281 78900
4.0921 3.8708 78900
==> ABCDE_8CHM.xyz <==
2.7118 4.0281 ABCDE
4.0921 3.8708 ABCDE
评论
file_list=( $(awk '{print "../glide-dock_SP_8CHM/ligands/"$0".sdf"}' ligand-list.txt) )
readarray -t ligand_list <ligand-list.txt; file_list=( "${ligand_list[@]/#/'../glide-dock_SP_8CHM/ligands/'}" ); file_list=( "${file_list[@]/%/.sdf}" )
readarray/mapfile
while/read/array+=()
评论
"$i"
for i in $(cat anything)
"$i"
bash -x yourscript
set -x
set +x
../glide-dock_SP_8CHM/ligands/ligand-list.txt.sdf
..
"$PWD"/../glide-dock_SP_8CHM/ligands/"$i".sdf