我有一个文本日志文件
$ cat aaa
673 20160405 root "/path_to/gis/20160401/20160301_placement_map_org.dbf" ""
673 20160405 root "/path_to/gis/20160401/20160310_20160401ent_map_org.dbf" ""
790890 20170201 jle "/path_to/gis/20160401/Pina (Asc) 20160401 Rapid Report.kmz" ""
5883710 20160406 dho "/path_to/gis/20160401/20160401_Pina_Asc_Rapid_Report_Minesouth.pdf" ""
673 20160405 dho "/path_to/gis/20160401/20160310_20160401 placement map org.dbf" ""
现在我的脚本仅输出文件的完整路径:
#!/bin/bash
function nodatechk() {
arr=("$@")
for ((i=3;i<${#arr[@]};i+=5));
do
echo "${i}" "${arr[i]}"
done
}
r=( $(grep gis aaa) )
nodatechk "${r[@]}"
输出是中断的,因为第三行(和第五行)在元素中有一个空格,尽管它有双引号。
我怎样才能解决这个问题? (顺便说一句,我知道我可以使用 awk 或 cut 来打印列,但在这种情况下我只想使用 grep。)谢谢。
答案1
问题的根源在于这一行:
r=( $(grep gis aaa) )
如果您尝试,您会立即看到:
printf '<%s>\n' $(grep gis aaa)
它会根据“$IFS”内的字符进行分割(默认情况下为空格、制表符、换行符)。
和将文件中的值公开给通配符。这将转换一些*
,?
和[…]
(哪些将取决于您的 pwd 上的文件列表和几个 shell 选项的条件)。
一种(不推荐)解决方案是更改IFS
为分割字符和禁用分割的通配:
IFS=$'\n'; set -f; r=( $(grep gis aaa) )
但更简单的解决方案是使用 shell 已经提供的功能:
readarray -t r <(grep gis aaa)
这将在换行符上分割(假设路径名中没有换行符)。
然后,为了避免再次分割每一行以获得可能使行暴露于空白分割和通配符的每个部分,让我们删除行的前导和尾随部分。
如果我们从每一行中删除从开头到"/
(双引号和斜杠)的所有内容以及从"
(双引号和空格)到结尾的所有内容,我们将得到一个干净的路径名:
#!/bin/bash
function nodatechk() {
for l do
l="/${l#*\"/}" # Remove leading text up to `"/`
l=${l%\" *} # Remove trailing text from `" `
printf '%s\n' "$l"
done
}
readarray -t r < <(grep gis aaa)
nodatechk "${r[@]}"
答案2
唯一grep
的解决方案是
grep gis aaa | grep -o '^[^"]*"[^"]*"' | grep -o '"[^"]*"$'
第一个grep
与您在问题中的内容相同。显然,它选择包含gis
(行中任何位置)的行。第二个 grep,
grep -o '^[^"]*"[^"]*"'
匹配到(并包括)该行中第一个带引号的字符串(即第 1 列到第 4 列)的所有内容,
并且,由于 -o
选项、输出仅有的那些话。 第三个 grep,
grep -o '"[^"]*"$'
匹配该行中最后一个带引号的字符串(此时,该字符串是原始行的第 4 列)并仅输出该字符串。
PS 如果您的文件在每对列之间有一个制表符,并且值不包含制表符,则获取第四列的简单方法是
awk -F'\t' '/gis/ { print $4 }' aaa