Remove the shortest spaces from all lines

I have text with some leading spaces on all lines. I want to remove the space from the shortest line (if it is easier, this requirement can be changed to the first line), and then remove the same amount of spaces from all the other lines.

eg. I have this text:

var flatten = function(result, next_array) { console.log('current result', result); return result.concat(next_array); }; [1, [2], [3, 4]] .reduce(flatten, []); 

And I want to bring to this text:

 var flatten = function(result, next_array) { console.log('current result', result); return result.concat(next_array); }; [1, [2], [3, 4]] .reduce(flatten, []); 

Basically, I want to move the text until there is one line left without spaces on the left and all the other leading spaces on all other lines remain.

A use case for this is to copy code from the middle of a code section for insertion as an example elsewhere. What I'm doing now is copy the code, paste into vim with paste paste mode , use << until I get the desired result, and copy the buffer. The same can be done in TextMate with Cmd - [ .

I want to do this with a shell script, so that I can, for example, run it with a hotkey to take the contents of the clipboard, remove the desired spaces and paste the result.

+6
source share
4 answers

this one line awk can do this for you too. it assumes that you want to remove at least 1 space. (because I see in your example an empty line without any leading spaces, but all lines are shifted anyway.)

check your example:

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

EDIT

I do not think awk scripts are not readable. but you should know the awk script syntax. Anyway, I add some explanation:

Awk script has two blocks, the first block was executed when each line of your file was read. The END block was executed after the last line of your file was read. See "Methods" below for an explanation.

 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 
+4
source

These functions can be defined in your .bash_profile so that you can access them anywhere and not create a script file. They do not require the first line to match:

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

Usage: shiftleft myfile.txt

This works with the file, but it will need to be modified to work with the pbpaste associated with it ...

NOTE. Definitely inspired by @JoSo's answer, but fixes bugs there. (Uses sort -r and cut -c N- and skips $ on len and does not get empty lines to hang without space.)


EDIT : OSX clipboard version:

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

Usage for this version: Copy the text of interest and enter shiftclip . To copy the output directly to the clipboard, do shiftclip | pbcopy shiftclip | pbcopy

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

Or, a little less readable, but avoiding sort overhead:

 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" 
+1
source

Hmmm ... this is not very pretty, and also assumes that you have access to Bash, but if you can live with the "first line" rule:

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

It also accepts only the space character (i.e. you need to customize the tabs), but you get the idea.

Assuming your example is in a snippet.txt file, put the bash code in a script (for example, "shiftleft"), a "chmod + x" script, then run with:

 shiftleft snippet.txt 
0
source

Source: https://habr.com/ru/post/955816/


All Articles