shell 删除所有行中的最短前导空格

3j86kqsm  于 2023-10-23  发布在  Shell
关注(0)|答案(5)|浏览(116)

我有一些文本,所有行上都有前导空格。我想从最短的一行中删除空白(如果更简单,这个要求可以改为第一行),然后从所有其他行中删除相同数量的空白。
例如,我有这样的文本:

var flatten = function(result, next_array) {
        console.log('current result', result);
        return result.concat(next_array);
    };

    [1, [2], [3, 4]]
        .reduce(flatten, []);

我想得到这段文字:

var flatten = function(result, next_array) {
    console.log('current result', result);
    return result.concat(next_array);
};

[1, [2], [3, 4]]
    .reduce(flatten, []);

基本上,我想移动文本,直到至少有一行左边没有空格,并在所有其他行上保留所有其他前导空格。
这种方法的用例是从一段代码的中间部分复制代码,作为示例粘贴到其他地方。我现在做的是复制代码,使用粘贴插入模式粘贴到Vim中,使用<<直到我得到想要的输出,然后复制缓冲区。在TextMate中使用Cmd-[也可以这样做。
我想要的是用一个shell脚本来做这件事,这样我就可以,例如,用一个热键触发它来获取我的剪贴板内容,删除所需的空白,然后粘贴结果。

z9smfwbn

z9smfwbn1#

这个awk一行程序也可以为你做这件事。它假定您要删除至少1个空格。(因为我在你的例子中看到,有一个空行,没有任何前导空格,但是所有的行都左移了。
用你的例子来测试:

kent$  cat f
    var flatten = function(result, next_array) {
        console.log('current result', result);
        return result.concat(next_array);
    };

    [1, [2], [3, 4]]
        .reduce(flatten, []);

kent$  awk  -F '\\S.*' '{l=length($1);if(l>0){if(NR==1)s=l; else s=s>l?l:s;}a[NR]=$0}END{for(i=1;i<=NR;i++){sub("^ {"s"}","",a[i]);print a[i]}}' f
var flatten = function(result, next_array) {
    console.log('current result', result);
    return result.concat(next_array);
};

[1, [2], [3, 4]]
    .reduce(flatten, []);

编辑

我不认为awk脚本不可读。但是你必须知道awk脚本的语法。无论如何,我补充一些解释:
awk脚本有两个块,第一个块在读取文件的每一行时执行。END块在读取文件的最后一行后执行。解释见下文评论。

awk  -F '\\S.*'          #using a delimiter '\\S.*'(regex). the first non-empty char till the end of line
                         #so that each line was separated into two fields,
                         #the field1: leading spaces
                         #and the field2: the rest texts

'{                       #block 1
l=length($1);            #get the length of field1($1), which is the leading spaces, save to l
if(l>0){                 #if l >0
if(NR==1)s=l;            #NR is the line number, if it was the first line, s was not set yet, then let s=l
else s=s>l?l:s;}         #else if l<s, let s=l otherwise keep s value
a[NR]=$0                 #after reading each line, save the line in a array with lineNo. as index
}                        #this block is just for get the number of "shortest" leading spaces, in s

END{for(i=1;i<=NR;i++){  #loop from lineNo 1-last from the array a
sub("^ {"s"}","",a[i]);  #replace s number of leading spaces with empty
print a[i]}              #print the array element (after replacement)
}' file                  #file is the input file
3npbholx

3npbholx2#

这些函数可以在.bash_profile中定义,以便在任何地方访问它们,而不是创建脚本文件。他们不需要第一行匹配:

shiftleft(){
    len=$(grep -e "^[[:space:]]*$" -v $1 | sed -E 's/([^ ]).*/x/' | sort -r | head -1 | wc -c)
    cut -c $(($len-1))- $1
}

用法:shiftleft myfile.txt
这适用于一个文件,但必须进行修改才能与pbpaste管道一起工作。
注意:绝对是受到@JoSo的答案的启发,但修复了其中的错误。(使用sort -rcut -c N-,len上缺少$,并且不会被空白行w/o白色空格挂起。

EDIT:在OSX上使用剪贴板内容的版本:

shiftclip(){
    len=$(pbpaste | grep -e "^[[:space:]]*$" -v | sed -E 's/([^ ]).*/x/' | sort -r | head -1 | wc -c)
    pbpaste | cut -c $(($len-1))-
}

此版本的用法:复制感兴趣的文本,然后键入shiftclip。要将输出直接复制回剪贴板,请执行shiftclip | pbcopy

nfeuvbwi

nfeuvbwi3#

len=$(sed 's/[^ ].*//' <"$file"| sort | head -n1 | wc -c)
cut -c "$((len))"- <"$file"

或者,可读性稍差,但避免了排序的开销:

len=$(awk 'BEGIN{m=-1} {sub("[^ ].*", "", $0); l = length($0); m = (m==-1||l<m) ? l : m; } END { print m+1 }' <"$file")
cut -c "$((len))"- <"$file"
kmb7vmvb

kmb7vmvb4#

嗯.这不是很漂亮,并且还假设您可以访问Bash,但是如果您可以接受您的“第一行”规则:

#!/bin/bash
file=$1
spaces=`head -1 $file | sed -re 's/(^[ ]+)(.+)/\1/g'`
cat $file | sed -re "s/^$spaces//"

它还假设只有空格字符(即,您需要调整制表符),但您可以理解。
假设你的例子在一个snippet.txt文件中,把bash代码放在一个脚本中(例如,“shiftleft”),“chmod +x”脚本,然后运行:

shiftleft snippet.txt
blmhpbnm

blmhpbnm5#

这是perl中的一行程序(尽管有点神秘):
给定一个带有缩进文本的文件(比如说**/tmp/indented.txt**):

var flatten = function(result, next_array) {
        console.log('current result', result);
        return result.concat(next_array);
    };

    [1, [2], [3, 4]]
        .reduce(flatten, []);

您可以使用以下命令修剪最大 * 公共 * 前导空格

perl -p -0777 -e '/^(\h+).*(\v(\1.*)?)*$/ && s/^$1//mg;' < /tmp/indented.txt

-0777将整个输入作为单个字符串。第一个正则表达式搜索常见的前导水平空格,第二个正则表达式从所有行中删除它。
输出现在看起来像:

var flatten = function(result, next_array) {
    console.log('current result', result);
    return result.concat(next_array);
};

[1, [2], [3, 4]]
    .reduce(flatten, []);

相关问题