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

时间:2013-10-11 23:59:48

标签: shell

我有一些文字在所有行上都有一些前导空格。我想从最短的行中删除空格(如果它更简单,可以将此要求更改为第一行),然后从所有其他行中删除相同数量的空格。

E.g。我有这样的文字:

    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, []);

基本上,我想将文本移到最左边,直到左边至少有一行没有空格,并保留所有其他行上的所有其他前导空格。

用例就是从代码段的中间复制代码以粘贴为其他地方的示例。我目前所做的是复制代码,使用paste insert mode粘贴到vim,使用<<直到我得到所需的输出,然后复制缓冲区。可以在TextMate中使用 Cmd - [

完成相同的操作。

我想要的是使用shell脚本执行此操作,以便我可以使用热键触发它以获取剪贴板内容,删除所需的空格并粘贴结果。

4 个答案:

答案 0 :(得分:4)

这个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

答案 1 :(得分:2)

可以在.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上缺少$并且不会被没有空格的空行挂断。)


编辑:在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

答案 2 :(得分:1)

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"

答案 3 :(得分:0)

嗯...这不是很漂亮,也假设您可以访问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