如何在两个 echo 命令的输出之间删除 \n?

Ali*_*Ali 13 shell shell-script

我有一个文本文件,每行包含一个文件名:

111_c4l5r120.png
123_c4l4r60.png
135_c4l4r180.png
147_c4l3r60.png
15_c4l1r120.png
...
Run Code Online (Sandbox Code Playgroud)

我想把它转换成这个形状:

111_c4l5r120.png 111
123_c4l4r60.png 123
135_c4l4r180.png 135
147_c4l3r60.png 147
15_c4l1r120.png 15
...
Run Code Online (Sandbox Code Playgroud)

使用此代码:

#!/bin/bash
while IFS='' read -r line || [[  -n "$line"  ]]; do
   echo "$line" >> output.txt   
   echo "$line" | cut -d'_' -f 1 >> output.txt
done < "$1"
Run Code Online (Sandbox Code Playgroud)

但是,结果是:

111_c4l5r120.png 
111
123_c4l4r60.png 
123
135_c4l4r180.png 
135
147_c4l3r60.png 
147
15_c4l1r120.png 
15
...
Run Code Online (Sandbox Code Playgroud)

我应该如何更改我的脚本以获得所需的输出?

ter*_*don 23

不要在shell中做这种事情!它比必要的复杂得多,容易出错,而且慢得多。有许多工具专为此类文本操作而设计。例如,在sed(这里假设最近的 GNU 或 BSD 实现-E):

$ sed -E 's/([^_]*).*/& \1/' file
111_c4l5r120.png 111
123_c4l4r60.png 123
135_c4l4r180.png 135
147_c4l3r60.png 147
15_c4l1r120.png 15
Run Code Online (Sandbox Code Playgroud)

或者,对于任何sed

$ sed 's/\([^_]*\).*/& \1/' file
111_c4l5r120.png 111
123_c4l4r60.png 123
135_c4l4r180.png 135
147_c4l3r60.png 147
15_c4l1r120.png 15
Run Code Online (Sandbox Code Playgroud)

珀尔:

$ perl -pe 's/(.+?)_.*/$& $1/' file
111_c4l5r120.png 111
123_c4l4r60.png 123
135_c4l4r180.png 135
147_c4l3r60.png 147
15_c4l1r120.png 15
Run Code Online (Sandbox Code Playgroud)

awk:

$ awk -F_ '{print $0,$1}' file
111_c4l5r120.png 111
123_c4l4r60.png 123
135_c4l4r180.png 135
147_c4l3r60.png 147
15_c4l1r120.png 15
Run Code Online (Sandbox Code Playgroud)

  • @ΈρικΚωνσταντόπουλος 是的。实际上快了几个数量级。外壳只是不太擅长这种事情。毕竟,shell 的主要工作是启动外部实用程序。将 OP 方法所花费的时间与此处任何解决方案所花费的时间进行比较。Shell 循环非常非常慢。如果您需要更有说服力,请阅读 [this](http://unix.stackexchange.com/a/169765/22222)。 (6认同)
  • @ΈρικΚωνσταντόπουλος Θα 'θελα :) 不,他恰好写了 2 个与两个评论线程相关的很棒的答案。至于可移植性,除了 perl 方法的(次要)例外,它只能在大约 90% 的 *nix 机器上工作,所有三种解决方案都是可移植的并且与 shell 无关。或者,好吧,你总是可以将 `sed` 文件变成 `sed 's/\([^_]*\).*/&amp; \1/' file` 以获得额外的可移植性。重点是,你可以指望 `awk` 和 `sed` 存在,而不是你可以指望其他任何东西。 (4认同)

Ste*_*itt 17

除非您特别需要为此使用 shell,否则 terdon 的回答提供了更好的选择。

由于您正在使用bash(如脚本的shebang所示),您可以使用该-n选项来回显:

echo -n "${line} " >> output.txt
echo "$line" | cut -d'_' -f 1 >> output.txt
Run Code Online (Sandbox Code Playgroud)

或者您可以使用 shell 功能来处理该行而不使用cut

echo "${line} ${line%%_*}" >> output.txt
Run Code Online (Sandbox Code Playgroud)

(替换两echo行)。

或者,printf也可以这样做,适用于任何POSIX shell,并且通常更好(有关详细信息,请参阅为什么 printf 比 echo 更好?):

printf "%s " "${line}" >> output.txt
echo "$line" | cut -d'_' -f 1 >> output.txt
Run Code Online (Sandbox Code Playgroud)

或者

printf "%s %s\n" "${line}" "${line%%_*}" >> output.txt
Run Code Online (Sandbox Code Playgroud)

(严格来说,简单地说/bin/shecho -n是不可移植的。因为你明确使用bash它在这里是可以的。)