从文件中删除前两行、最后两行和 space 并在每行上添加引号并在 shell 脚本中用逗号替换换行符

Remove first two lines, last two lines and space from file and add quotes on each line and replace newline with commas in shell script

我必须 input.txt 需要使用 shell 脚本格式化的文件,条件如下

  1. 删除前两行和 最后两行
  2. 删除每个中的所有 space 行(每行有两个 spaces 在 开头和结尾一个 space)
  3. 每行应在单个内 引号(' ')
  4. 最后将换行符($)替换为 逗号.

(原创) input.txt

 sql
--------
  Abce
  Bca
  Efr
-------
Row (3)

所需的输出文件

output.txt

'Abce','Bca','Efr'

我试过使用以下命令

Sed -i 1,2d input.txt > input.txt
Sed "$(( $(wc -l <input.txt) -2+1)), $ d" Input.txt > input.txt
Sed ':a;N;$!ba;s/\n/, /g' input.txt > output.txt

但我一片空白output.txt

第一个 sed -i 用空文件覆盖 input.txt。您不能将输出写回您正在读取的文件,而且 sed -i 无论如何也不会产生任何输出。

最小的修复方法是删除 -i 并将命令串在一起进入管道;但当然,sed 允许您将命令合并到一个脚本中。

len=$(wc -l <input.txt)
sed -e '1,2d' -e "$((len - 3))"',$d' \
    -e ':a' \
    -e 's/^  \(.*\) $/'"'\1'/" \
    -e N -e '$!ba' -e 's/\n/, /g' input.txt >output.txt

(未经测试;如果您的 sed 不允许多个 -e 选项,则需要重构以在命令之间使用带有分号或换行符的单个字符串。)

这很难编写和调试,而且很脆弱,因为您必须将 shell 的引用功能与 sed 和这个特定脚本的要求相结合,但也更固有因为 sed 是一种简洁晦涩的语言。

一个更易读和可维护的解决方案是切换到 Awk,它允许您以更人性化的方式表达逻辑,并且避免必须从 shell 获得对算术和算术等简单任务的支持字符串格式。

awk 'FNR > 2 { sub(/^  /, ""); sub(/ $/, "");
    a[++i] = sprintf("7%s7,", [=11=]); }
    END { for(j=1; j < i-1; ++j) printf "%s", a[j] }' input.txt >output.txt

这实际上用逗号替换了所有换行符;也许您实际上想在最后一行打印一个换行符而不是逗号?

awk 'FNR > 2 { sub(/^  /, ""); sub(/ $/, "");
    a[++i] = sprintf("%s7%s7", sep, [=12=]); sep="," }
    END { for(j=1; j < i-1; ++j) printf "%s", a[j]; printf "\n" }' input.txt >output.txt

如果输入文件非常大,您可能需要重构它以不将所有行都保留在内存中。数组 a 收集格式化输出,我们在 END 块中打印除最后两个元素之外的所有元素。

我会 post 一个相当轻便的 sed 解决方案。

sed '$d' input.txt | sed "$d; 1,2d; s/^\s*\|\s*$/'/g" | paste -sd ',' > output.txt
  • $d 删除第一个 sed 的最后一行
  • $d 删除最后一行。 $ 使用反斜杠转义,因为我们在 double-quotes.
  • 1,2d 删除前两行。
  • s/^\s*\|\s*$/'/g 用单引号替换所有前导和尾随空格。
  • 使用 paste 连接成一个逗号分隔的字符串。

如果我们知道相关行总是以两个空格开头,那么它甚至可以进一步简化。

sed -n "s/\s*$/'/; s/^  /'/p" input.txt | paste -sd ',' > output.txt
  • -n 禁止打印行,除非被告知
  • s/\s*$/'/ 用单引号替换尾随空格
  • s/^ /'/p 替换两个前导空格并打印匹配
  • 的行
  • paste 到 concat

然后一个awk解决方案:

awk -v i=1 -v q=\' 'FNR>2 {
    gsub(/^[[:space:]]*|[[:space:]]*$/, q)
    a[i++]=[=12=]
} END {
    for(i=1; i<=length(a)-3; i++)
        printf "%s,", a[i]
    print a[i++]
}' input.txt > output.txt
  • -v i=1 创建一个从 one
  • 开始的 awk 变量
  • -v q=\' 为单引号字符创建一个 awk 变量
  • FNR>2 { ... 告诉它只处理第 3+
  • gsub(/^[[:space:]]*|[[:space:]]*$/, q) 用单引号替换前导和尾随空格
  • a[i++]=[=27=] 向数组添加行
  • END { ... 到达文件末尾后处理剩余部分
  • for(i=1; i<=length(a)-3; i++) 取数组的长度减去三——代表最后三行
  • printf "%s,", a[i] 打印除最后三个条目以外的所有条目,逗号分隔
  • print a[i++] 打印下一个条目并完成脚本(跳过最后两个条目)
sed -E '
/^-+$/,/^-+$/!d
//d
s/^[[:space:]]*|[[:space:]]*$/'\''/g
' input.txt |
paste -sd ,
  • 这使用了一种并非对所有 sed 实现都有效的技巧,打印两个模式之间的线条(在本例中为破折号),排除 那些模式。
  • 从好的方面来说,如果 ---- 模式位于不同的行号,它仍然有效。不利的一面是,如果该模式(仅包含破折号的一行)出现奇数次(即不成对出现,则换行您想要的行)。
  • 然后子行开始和结束(包括白色space)用单引号。
  • 最后通过管道传送到 paste 以用逗号分隔新行,不包括尾随逗号。

使用您显示的示例,请尝试执行以下 awk 程序。在 GNU awk 中编写和测试,应该适用于任何版本。

awk -v s1="'" -v lines="$(wc -l < Input_file)" '
BEGIN{ OFS="," }
FNR==(lines-1) {
  print val
  exit
}
FNR>2{
  sub(/^[[:space:]]+/,"")
  val=(val?val OFS:"") (s1 [=10=] s1)
}
' Input_file

说明:对以上代码添加详细说明,仅供说明。

awk -v s1="'" -v lines="$(wc -l < Input_file)" '  ##Starting awk program, setting s1 variable to ' and creating lines which has total number of lines in it, using wc -l command on Input_file file.
BEGIN{ OFS="," }                                ##Setting OFS to comma in BEGIN section of this program.
FNR==(lines-1) {                                ##Checking condition if its 2nd last line of Input_file.
  print val                                     ##Then printing val here.
  exit                                          ##exiting from program from here.
}
FNR>2{                                          ##Checking condition if FNR is greater than 2 then do following.
  sub(/^[[:space:]]+/,"")                       ##Substituting initial spaces with NULL here.
  val=(val?val OFS:"") (s1 [=11=] s1)               ##Creating val which has ' current line ' in it and keep adding it in val.
}
' Input_file                                    ##Mentioning Input_file name here.

请您尝试以下操作:

mapfile -t ary < <(tail -n +3 input.txt | head -n -2 | sed -E "s/^[[:blank:]]*/'/; s/[[:blank:]]*$/'/")
(IFS=,; echo "${ary[*]}")
  • tail -n +3 输出第 3 行之后的行,包括在内。
  • head -n -2 输出不包括最后 2 行的行。
  • sed -E "s/^[[:blank:]]*/'/" 删除前导空格和前缀 单引号。
  • 类似地,sed 命令 "s/[[:blank:]]*$/'/" 删除尾随 空格并附加一个单引号。
  • 语法 <(command ..) 是进程替换, 括号内命令的输出被馈送到 mapfile 通过重定向。
  • mapfile -t ary 从标准输入读取行到数组 名为 ary.
  • 的变量
  • echo "${ary[*]}" 扩展为包含以下内容的单个字符串 数组 ary 由刚刚分配的 IFS 的值分隔 逗号。
  • IFS的赋值和数组展开用 要在子 shell 中执行的括号。这可以防止 IFS 在当前进程中修改。

使用sed

$ sed "1,2d; /-/,$ d; s/\s\+//;s/.*/'&'/" input_file | sed -z 's/\n/,/g;s/,$/\n/'
'Abce','Bca','Efr'

如果您知道输入足够小以适合内存:

$ awk '
    NR>4 { gsub(/^ *| *$/,"7",p2); out=out sep p2; sep="," }
    { p2=p1; p1=[=10=] }
    END { print out }
' input.txt
'Abce','Bca','Efr'

否则:

$ awk '
    NR>4 { gsub(/^ *| *$/,"7",p2); printf "%s%s", sep, p2; sep="," }
    { p2=p1; p1=[=11=] }
    END { print "" }
' input.txt
'Abce','Bca','Efr'

任何一个脚本都可以在每个 Unix 机器上使用任何 shell 中的任何 awk。

这可能对你有用 (GNU sed):

sed -E '1,2d;$!H;$!d;x;s/^\s*(.*)\s*$/'\'''\''/mg;s/\n[^\n]*$//;y/\n/,/' file

删除前两行。

将每一行附加到保留 space,最后一行除外(这意味着倒数第二行仍将存在 - 见下文)。

删除除最后一行以外的所有行。

切换到保留 space。

删除每行单词两侧的所有 space 并用单引号将这些单词括起来。

删除最后一行及其换行符。

用逗号替换所有换行符。

不是一个班轮但有效

sed "s/^ */\'/;s/$/\',/;1,2d;N;$!P;$!D;$d"  | sed ' H;1h;$!d;x;s/\n//g;s/,$//'

解释:

s/^ */\'/;s/$/\',/ ---> 添加单引号和逗号

N;$!P;$!D;$d ---> 删除最后两行

H;1h;$!d;x;s/\n//g;s/,$//' ---> 加载整个文件并合并所有行并删除最后一个逗号