6

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

例如,我有这样的文字:

    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

4 回答 4

4

这个 awk one-liner 也可以为你做这件事。它假定您要删除至少 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
于 2013-10-12T09:58:40.780 回答
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 答案的启发,但修复了那里的错误。(在 len 上使用sort -rcut -c N-缺少 $ 并且不会被没有空格的空行挂起。)


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

于 2013-10-12T07:59:32.147 回答
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"
于 2013-10-12T01:50:30.967 回答
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
于 2013-10-12T01:00:39.303 回答