我有一个或多或少像这样的变量:
$ echo "$LIST"
file1: ok
file2: ok
file3:
file4:
file5: ok
然后我需要获取不正常的文件列表:
$ sed '/:\s.\+$/d' <<< "$LIST"
file3:
file4:
该文件有效,但如果列表中没有不正常的文件,则会发生这种情况:
$ echo "$LIST"
file1: ok
file2: ok
file3: ok
file4: ok
file5: ok
$ sed '/:\s.\+$/d' <<< "$LIST"
$ NEWLIST=$(sed '/:\s.\+$/d' <<< "$LIST")
$ echo "$NEWLIST"
$ cat -A <<< "$NEWLIST"
$
$ wc -l <<< "$NEWLIST"
1
$ wc -c <<< "$NEWLIST"
1
添加到变量中的这一换行符(我认为是 \n)使我的程序变得一团糟,因为它标识为列出了一个文件,因为我用来wc -l
知道那里存在多少个文件。我不完全确定 \n 是由 bash 还是由 sed 分配。有人知道解决方法吗?
答案1
如果您只想排除ok
项目,您可以这样做:
grep -cv ': ok$' <<< "$LIST"
或者类似,但大致相反
grep -c ':$' <<< "$LIST"
编辑:基于@ilkkachu 的评论
如果列表完全为空,则-vc
由于某种行为,变体将错误地报告计数为 1 <<<
。
您可以添加一个防护来检测空列表,也可以简单地使用管道
printf "%s" "$LIST" | grep -vc ": ok$"
如果列表可能包含空行,那么在使用时也会导致计数错误-vc
。
在任何一种情况下,都可以进行进一步的修改以防止错误计数。
grep -vcE "^$|: ok$"
但现在我们开始跳过障碍,从而使代码更难理解。
答案2
Here-string<<<
在将字符串传递给命令之前在字符串末尾添加换行符,而命令替换会在读取内部命令的输出后删除所有尾随换行符。
另外,echo
在打印的内容中添加尾随换行符,但这不是这里的主要问题。
因此,假设变量中有一个完整的行,末尾有换行符,所以字符串是file1: ok<nl>
现在,您运行sed '/ok/d' <<< "$LIST"
,并在这里sed
获取输入file1: ok<nl><nl>
。它删除任何包含 的行ok
,并输出空行<nl>
。
您可以通过命令替换来捕获它,该替换会删除尾随的换行符,给出空字符串。然后将其分配给NEWLIST
.
然后,echo "$NEWLIST"
打印一个换行符(因为echo
添加了一个),并wc -l <<< "$NEWLIST"
给出一个换行符作为输入wc
(因为here-string添加了一个)。
如果变量最初只是file1: ok
,没有尾随换行符,则仅命令替换不会从 sed 输出的末尾删除换行符,您将得到相同的最终结果。
所有这些意味着命令替换和此处字符串大多适用于单行值,您可能不希望换行符出现在变量中,但通常确实希望提供带有完整行作为输入的任何命令。正如您所看到的,它们也适用于多行字符串(如果变量中再次缺少最终换行符),但换行符的奇怪删除和添加仍然会发生。如果没有要删除的换行符,它就会崩溃。
要了解为什么要进行这种杂耍,请注意,如果命令替换没有从此处的输出中删除换行符date
,则这会在末尾的句点之前打印一个换行符,从而打破中间的行。
$ weekday=$(date +%A)
$ echo "today is a $weekday."
today is a Thursday.
最简单的解决方法可能只是将多行数据存储在文件中。包含inputfile
五行(带有适当的换行符):
file1: ok
file2: ok
file3: ok
file4: ok
file5: ok
然后:
tmpfile=$(mktemp)
sed -e '/ok/d' < inputfile > "$tmpfile"
wc -l < "$tmpfile"
rm -f "$tmpfile"
输出0
。
(请注意,\s
或都不\+
是标准的 POSIX 基本或扩展正则表达式语法,因此它们不适用于所有系统,例如 macOS sed
。这就是我在/ok/
上面使用的原因。)
答案3
我不完全确定 \n 是由 bash 还是由 sed 分配。
目前两者都不是。例如,您可以检查它
$ echo -n "$NEWLIST" # echo without -n adds a newline
$ wc -l <<< ""
1
$ wc -c <<< ""
1
它是echo
故意打印你的变量(它是空的)并以换行符结尾,对于这里字符串,Bash 确保它以换行符结尾,因为如果最后一行不以 \n 结尾,工具往往会表现得令人惊讶。
解决这个问题最简单的方法可能就是检查 NEWLIST 是否为空。或者直接更多地处理文件。例如:
list_file="$(mktemp)"
new_list_file="$(mktemp)"
# cleanup
trap 'rm "$list_file" "$new_list_file"' EXIT
echo "$LIST" > "$list_file"
sed '/:\s.\+$/d' "$list_file" > "$new_list_file"
wc -l "$new_list_file"
# or wc -l < "$new_list_file" if you want to prevent it from printing the filename
作为意外结果的示例,如果此处字符串不会添加换行符,请考虑以下命令的预期结果,然后运行它:
echo -n "Content" | wc -l
答案是:0