我有一个代码:
1 /**
2 a b c
3 **/
4 int main() {
5 int x;
6 if ( condition) {
7 return x;
8 }
9 }
我需要将标记或字符串之间的多个空格更改为单个空格,例如在第 7 行中,但注释(第 2 行)不应受到影响,代码中的前导选项卡也不应受到影响。所以,输出应该是:
1 /**
2 a b c
3 **/
4 int main() {
5 int x;
6 if ( condition) {
7 return x;
8 }
9 }
我尝试使用 'tr':~$ tr -s " " < file
但它更改了第 2 行,并删除了第 5 行至第 8 行中的前导制表符。可以使用 来完成吗sed
?
答案1
会走多远
sed -rn '\#/\*\*#,\#\*\/*# {p;b}; s/([^ ]) +/\1 /g; p' file
我懂了?它打印未修改的注释行(但不能处理同一行中的注释打开/关闭)并跳过脚本的其余部分。对于未注释的行,它将非空格字符后面的任何多个空格(因此行缩进超出范围)压缩为单个空格。
答案2
最后,这对我有用:
sed -i 's/\([a-zA-Z]\+\)\( *\)\([a-zA-Z]\+\)/\1 \3/g' $1
答案3
使用乐(以前称为 Perl_6)
~$ raku -pe 's/^ [\d+ " "?] \t* \H+? <(" " ** 2..*)> / /;' file
#OR
~$ raku -pe 's/^ [\d+ " "?] \c[TAB]* \H+? <(\c[SPACE] ** 2..*)> / /;' file
上面的 Raku 代码从字面上区分了制表符(\t
或)\c[TAB]
和空格(" "
或\c[SPACE]
)。
这些-pe
标志在输入上逐行运行代码,并具有自动打印功能。正则表达式从^
字符串的开头搜索[\d+ " "?]
数字,后跟 0 或 1 空格,后跟可选制表符,后跟(非贪婪地)\H+?
一个或多个非水平空白字符,最后是 2..*
两个或多个空格。<(
...捕获)>
标记会删除匹配中的所有内容,除了最后两个或更多空格,这些空格将替换为单个空格。
[\d+ " "?]
如果没有行号,则省略括号内的组。每行只有一次替换,因此如果行右端有多个空格字符,则可能需要运行多次。
输入示例:
1 /**
2 a\tb\tc
3 **/
4 int main() {
5\tint x;
6\tif ( condition) {
7\treturn x;
8\t}
9 }
示例输出:
1 /**
2 a\tb\tc
3 **/
4 int main() {
5\tint x;
6\tif ( condition) {
7\treturn x;
8\t}
9 }
当然,上面的答案是基于注释块中的字符由制表符分隔,而不是多个空格。也许是确保的最好方法那是为了预运行具体替换注释块:
~$ raku -pe 'state $ph; \
$ph = 1 if /^ "/**" \s* $/; \
$ph = 0 if /^ "**/" \s* $/; \
s:g/" " ** 4/\t/ if $ph == 1 ;' file
上面的代码将每 4 个空格替换一个制表符,仅限在注释块内。如果每行都以行号开头,[\d+ " "?]
则添加回第二个和第三个语句中的括号组中。^
https://unix.stackexchange.com/a/701572/227738
https://docs.raku.org/language/regexes
https://raku.org