我有一个表格文件
2e95d7582c53583fa8afb54e0fe7a2597c92cbba 1461065389 52880 temp/hello/file.txt
46c897a7aa8a641f46080b3431860bd0cd4a8f05 1461066221 207 temp/Another file.txt
83c8ce6b163ec1c615617fa0dbde9e928bc3daf4 1461056193 86112 Pictures/a photo.jpg
...
也就是说,每一行都有一个 40 个字符长的十六进制数字,后跟空格、整数、空格、整数、空格以及可能包含空格的文件路径。每条道路都是独一无二的。
在 bash 脚本中,我的变量与文件中的一行具有相同的形式,只是没有第一个十六进制字符串和空格(第一个 41 个字符),例如:
myvar="1461066221 207 temp/Another file.txt"
我的目标是找到这文件中匹配的行myvar
确切地当前 41 个字符被忽略时。如果找到这样的匹配,我希望将变量line
设置为文件中的整行。对于上面的示例,line
将设置为
46c897a7aa8a641f46080b3431860bd0cd4a8f05 1461066221 207 temp/Another file.txt
如果没有这样的匹配,line
应该设置为空字符串,或者不设置。
我的解决方案是这样的(filelist
是文件名):
line=$(grep --color=never -E "^.{41}$myvar$" $filelist)
一个问题是,当$myvar
展开时,它可能包含特殊符号,如.
、+
、 Even^
或$
等,这些符号对 具有特殊含义grep
。我想grep
表演一个精确的匹配myvar
一行中除前 41 个字符之外的所有字符。
答案1
Perl 来救援!
perl -ne 'BEGIN { $search = shift }
print if /^.{41}\Q$search\E$/;
' -- "$myvar" "$filelist"
-n
逐行读取文件。- BEGIN 块将第一个参数中的 $myvar 检索到 Perl 变量 $search 中。
\Q...\E
引用内部部分(参见引用元)。这处理变量可以包含的所有特殊字符。不要忘记双引号 shell 变量!
答案2
export myvar
awk 'substr($0, 42) == ENVIRON["myvar"]' < "$filelist"
答案3
这是一条不同的路;它首先将 'line' 设置为空,如果发现 'myvar' 与尾随 3 个字段匹配则设置它。
line=
while IFS=' ' read -r hex int1 int2 rest
do
if [[ "$myvar" = "$int1 $int2 $rest" ]]
then
line="$hex $int1 $int2 $rest"
fi
done < filelist
这是另一种方法,使用 bash 的mapfile
内置和第二个临时关联数组:
mapfile -t < filelist # sets MAPFILE array
declare -A temparray
shopt -s extglob
for val in "${MAPFILE[@]}"
do
short="${val##*([^ ]) }"
temparray[$short]="$val"
done
line=${m[$myvar]}
unset -v MAPFILE temparray val short
这会将文件放入索引数组 MAPFILE 中,然后我们对其进行循环。该循环将“short”变量设置为该行的“后缀”部分 - 它删除与“零个或多个非空格后跟空格”模式匹配的前导值,然后设置“$”的关联数组值短”为整行(“$val”)。进行这种匹配需要*
生效shopt -s extglob
。然后我们将 line 设置为结果值(或者如果该值不存在于数组中,bash 将返回空/空值)。