我想从文件中删除所有的空行,但只有当它们在文件的结尾/开始时(也就是说,如果在它们之前没有非空行,则在开头;如果有在他们之后没有非空行,最后。)
这可能是一个function齐全的脚本语言,如Perl或Ruby? 如果可能的话,我宁愿用sed
或awk
来完成。 基本上,任何轻量级和广泛使用的UNIX-y工具都可以,尤其是我可以快速了解更多(Perl,因此不包括在内)。
从有用的单行脚本sed :
# Delete all leading blank lines at top of file (only). sed '/./,$!d' file # Delete all trailing blank lines at end of file (only). sed -e :a -e '/^\n*$/{$d;N;};/\n$/ba' file
因此,要从文件中删除前导空行和尾行空行,可以将上述命令组合到:
sed -e :a -e '/./,$!d;/^\n*$/{$d;N;};/\n$/ba' file
所以我要借用@ dogbane的部分回答,因为删除前导空行的sed
行很短…
tac
是coreutils的一部分 ,并反转文件。 所以做两遍:
tac file | sed -e '/./,$!d' | tac | sed -e '/./,$!d'
这当然不是最有效率的,但是除非你需要效率,否则我发现它比其他所有东西都更具可读性。
这里是awk中的一个通行解决方案:它不会开始打印,直到它看到一个非空行,并且当它看到一个空行时,它会记住它,直到下一个非空行
awk ' /[[:graph:]]/ { # a non-empty line # set the flag to begin printing lines p=1 # print the accumulated "interior" empty lines for (i=1; i<=n; i++) print "" n=0 # then print this line print } p && /^[[:space:]]*$/ { # a potentially "interior" empty line. remember it. n++ } ' filename
请注意,由于我使用的机制考虑空/非空行( [[:graph:]]
和/^[[:space:]]*$/
),只有空格的内部行将被截断变得真正的空虚。
使用awk:
awk '{a[NR]=$0;if($0 && !s)s=NR;} END{e=NR; for(i=NR;i>1;i--) if(a[i]){ e=i; break; } for(i=s;i<=e;i++) print a[i];}' yourFile
正如另一个答案中提到的, tac
是coreutils的一部分 ,并反转一个文件。 将两次做这个想法和命令替换将剥去拖尾新行的事实相结合,我们就可以得到
echo "$(echo "$(tac "$filename")" | tac)"
这不依赖于sed
。 你可以使用echo -n
去除剩下的换行符。
这是一个改编的sed版本,它也认为这些行只有空格和制表符。
sed -e :a -e '/[^[:blank:]]/,$!d; /^[[:space:]]*$/{ $d; N; ba' -e '}'
这基本上是接受的答案版本(考虑BryanH评论),但点.
在第一个命令中被改为[^[:blank:]]
(任何东西都不为空),而第二个命令地址中的\n
更改为[[:space:]]
以允许换行符,空格标签。
另一个版本,不使用POSIX类,但你的sed必须支持在\t
[…]
内插入\t
和\n
。 GNU sed呢,BSD sed没有。
sed -e :a -e '/[^\t ]/,$!d; /^[\n\t ]*$/{ $d; N; ba' -e '}'
测试:
prompt$ printf '\n \t \n\nfoo\n\nfoo\n\n \t \n\n' foo foo prompt$ printf '\n \t \n\nfoo\n\nfoo\n\n \t \n\n' | sed -nl $ \t $ $ foo$ $ foo$ $ \t $ $ prompt$ printf '\n \t \n\nfoo\n\nfoo\n\n \t \n\n' | sed -e :a -e '/[^[:blank:]]/,$!d; /^[[:space:]]*$/{ $d; N; ba' -e '}' foo foo prompt$
使用bash
$ filecontent=$(<file) $ echo "${filecontent/$'\n'}"
在bash中,使用cat,wc,grep,sed,tail和head:
# number of first line that contains non-empty character i=`grep -n "^[^\B*]" <your_file> | sed -e 's/:.*//' | head -1` # number of hte last one j=`grep -n "^[^\B*]" <your_file> | sed -e 's/:.*//' | tail -1` # overall number of lines: k=`cat <your_file> | wc -l` # how much empty lines at the end of file we have? m=$(($k-$j)) # let strip last m lines! cat <your_file> | head -n-$m # now we have to strip first i lines and we are done 8-) cat <your_file> | tail -n+$i
男人,学习“真正的”编程语言来避免这种丑陋是绝对值得的!
一个bash
解决方案 。
注意:只有在文件足够小以便一次读入内存时才有用。
[[ $(<file) =~ ^$'\n'*(.*)$ ]] && echo "${BASH_REMATCH[1]}"
$(<file)
读取整个文件并修剪尾随的换行符,因为命令替换( $(....)
) 隐式地执行该操作。 =~
是bash的正则表达式匹配运算符 ,并且=~ ^$'\n'*(.*)$
可选地匹配任何前导换行符(贪婪地),并捕获之后的任何内容。 注意可能混淆的$'\n'
,它使用ANSI C引用插入一个文字换行符,因为不支持转义序列\n
。 &&
之后的命令总是被执行。 BASH_REMATCH
包含最近一次正则表达式匹配的结果,而数组元素[1]
包含(被第一个也是唯一)括号化的子表达式(捕获组)捕获的内容,这是除去了任何前导换行符的输入字符串。 最终的结果是${BASH_REMATCH[1]}
包含了输入文件内容,并且剥去了前导和${BASH_REMATCH[1]}
换行符。 echo
打印会添加一个尾随的换行符。 如果您想避免这种情况,请改用echo -n
(或使用更便携的printf '%s'
)。 我想介绍一下gawk v4.1 +的另一个版本
result=($(gawk ' BEGIN { lines_count = 0; empty_lines_in_head = 0; empty_lines_in_tail = 0; } /[^[:space:]]/ { found_not_empty_line = 1; empty_lines_in_tail = 0; } /^[[:space:]]*?$/ { if ( found_not_empty_line ) { empty_lines_in_tail ++; } else { empty_lines_in_head ++; } } { lines_count ++; } END { print (empty_lines_in_head " " empty_lines_in_tail " " lines_count); } ' "$file")) empty_lines_in_head=${result[0]} empty_lines_in_tail=${result[1]} lines_count=${result[2]} if [ $empty_lines_in_head -gt 0 ] || [ $empty_lines_in_tail -gt 0 ]; then echo "Removing whitespace from \"$file\"" eval "gawk -i inplace ' { if ( NR > $empty_lines_in_head && NR <= $(($lines_count - $empty_lines_in_tail)) ) { print } } ' \"$file\"" fi
@dogbane有一个很好的简单的答案来删除前导空行。 这是一个简单的awk命令,它只删除尾随的行。 使用@ dogbane的sed命令来删除前导空白和尾随空白。
awk '{ LINES=LINES $0 "\n"; } /./ { printf "%s", LINES; LINES=""; }'
这在操作上非常简单。
所以唯一被缓冲而且从不显示的是任何尾随的空白。
我使用printf而不是print来避免自动添加换行符,因为我正在使用换行符来分隔缓冲区中的行。
对于一个高效的非递归版本的尾随换行条(包括“白色”字符),我已经开发了这个sed
脚本。
sed -n '/^[[:space:]]*$/ !{x;/\n/{s/^\n//;p;s/.*//;};x;p;}; /^[[:space:]]*$/H'
它使用保持缓冲区来存储所有的空白行,并只有在找到非空白行后才打印它们。 如果有人只需要换行符,就足以摆脱这两个[[:space:]]*
部分:
sed -n '/^$/ !{x;/\n/{s/^\n//;p;s/.*//;};x;p;}; /^$/H'
我已经尝试了一个简单的性能比较着名的递归脚本
sed -e :a -e '/^\n*$/{$d;N;};/\n$/ba'
在一个随机的空白行周围有一个随机的base64文本的3MB文件。
shuf -re 1 2 3 | tr -d "\n" | tr 123 " \t\n" | dd bs=1 count=1M > bigfile base64 </dev/urandom | dd bs=1 count=1M >> bigfile shuf -re 1 2 3 | tr -d "\n" | tr 123 " \t\n" | dd bs=1 count=1M >> bigfile
流脚本花了大约0.5秒完成,递归15分钟后没有结束。 赢:)
为了答案的完整性,剥离sed脚本的引导行已经很好地流了。 使用最适合你的。
sed '/[^[:blank:]]/,$!d' sed '/./,$!d'