perl -lane change delimiter - perl

I have one liner:
az account list -o table |perl -lane 'print $F[0] if /GS/i'
I want to change default delimiter from '\t' to '-'
Any hint how to do this?
Just wanted to stress that it is oneliner I look for ;)

Plain -a splits on any whitespace, not just tab. The -F option allows you to specify a different delimiter.
az account list -o table |
perl -laF- -ne 'print $F[0] if /GS/i'

perlrun is the manual page that tells you about Perl's command-line options. It says:
-a
turns on autosplit mode when used with a "-n" or "-p". An implicit split command to the #F array is done as the first thing inside the implicit while loop produced by the "-n" or "-p".
perl -ane 'print pop(#F), "\n";'
is equivalent to
while (<>) {
#F = split(' ');
print pop(#F), "\n";
}
An alternate delimiter may be specified using -F.
And:
-Fpattern
specifies the pattern to split on for "-a". The pattern may be surrounded by //, "", or '', otherwise it will be put in single quotes. You can't use literal whitespace or NUL characters in the pattern.
-F implicitly sets both "-a" and "-n".

Related

Insert linebreak in a file after a string

I have a unique (to me) situation:
I have a file - file.txt with the following data:
"Line1", "Line2", "Line3", "Line4"
I want to insert a linebreak each time the pattern ", is found.
The output of file.txt shall look like:
"Line1",
"Line2",
"Line3",
"Line4"
I am having a tough time trying to escape ", .
I tried sed -i -e "s/\",/\n/g" file.txt, but I am not getting the desired result.
I am looking for a one liner using either perl or sed.
You may use this gnu sed:
sed -E 's/(",)[[:blank:]]*/\1\n/g' file.txt
"Line1",
"Line2",
"Line3",
"Line4"
Note how you can use single quote in sed command to avoid unnecessary escaping.
If you don't have gnu sed then here is a POSIX compliant sed solution:
sed -E 's/(",)[[:blank:]]*/\1\
/g' file.txt
To save changes inline use:
sed -i.bak -E 's/(",)[[:blank:]]*/\1\
/g' file.txt
Could you please try following. using awk's substitution mechanism here, in case you are ok with awk.
awk -v s1="\"" -v s2="," '{gsub(/",[[:blank:]]+"/,s1 s2 ORS s1)} 1' Input_file
Here's a Perl solution:
perl -pe 's/",\K/\n/g' file.txt
The substitution pattern matches the ",, but the \K says to ignore anything to the left for the replacement (so, ",) will not be replaced. The replacement then effectively inserts the newline.
I used the single quote for the argument to -e, but that doesn't work on Windows where you have to use ". Instead of escaping the ", you can specify it in another way. That's code number 0x22, so you can write:
perl -pe "s/\x22,\K/\n/g" file.txt
Or in octal:
perl -pe "s/\042,\K/\n/g" file.txt
Use this Perl one-liner:
perl -F'/"\K,\s*/' -lane 'print join ",\n", #F;' in_file > out_file
Or this for in-line replacement:
perl -i.bak -F'/"\K,\s*/' -lane 'print join ",\n", #F;' in_file
The Perl one-liner uses these command line flags:
-e : Tells Perl to look for code in-line, instead of in a file.
-n : Loop over the input one line at a time, assigning it to $_ by default.
-l : Strip the input line separator ("\n" on *NIX by default) before executing the code in-line, and append it when printing.
-a : Split $_ into array #F on whitespace or on the regex specified in -F option.
-F'/"\K,\s*/' : Split into #F on a double quote, followed by comma, followed by 0 or more whitespace characters, rather than on whitespace. \K : Cause the regex engine to "keep" everything it had matched prior to the \K and not include it in the match. This causes to keep the double quote in #F elements, while comma and whitespace are removed during the split.
-i.bak : Edit input files in-place (overwrite the input file). Before overwriting, save a backup copy of the original file by appending to its name the extension .bak.
SEE ALSO:
perldoc perlrun: how to execute the Perl interpreter: command line switches
perldoc perlrequick: Perl regular expressions quick start

Data transformation using sed

I have a file like:
A
B
C
D
E
F
G
H
I
J
K
L
and I want it to come out like
A,B,C,D
E,F,G,H
I'm assuming I'd use sed, but actually I'm not even sure if that's the best tool. I'm open to using anything commonly available on a Linux system.
In perl, I did it like this ... it works, but it's dirty and has a trailing comma. Was hoping for something simpler:
$ perl -ne 'if (/^(\w)\R/) {print "$1,";} else {print "\n";}' test
A,B,C,D,
E,F,G,H,
I,J,K,L,
Set the input record separator to paragraph mode (-00) and then split each record on any remaining whitespace:
$ perl -00 -ne 'print join("," => split), "\n"' test
Add -l to enable automatic newlines (but make sure it comes before -00, because we want $\ to be set to the value of $/ before modification):
$ perl -l -00 -ne 'print join("," => split)' test
Add -a to enable autosplit mode and implicitly split to #F:
$ perl -l -00 -ane 'print join("," => #F)' test
Swap out -n for -p for automatic printing:
$ perl -l -00 -ape '$_ = join("," => #F)' test
You could use
awk 'BEGIN {RS=""; FS="\n"; ORS="\n"; OFS=","} {$1=$1} 1' file
I see the gawk manual says this:
If RS
is set to the null string, then records are separated by blank lines. When RS is set to the null string, the newline character always acts as a field separator, in addition to whatever value FS may have.
So we don't actually need to specify FS to get the desired output:
awk 'BEGIN {RS=""; ORS="\n"; OFS=","} {$1=$1} 1' file
xargs could do it,
$ xargs -n4 < file | tr ' ' ','
A,B,C,D
E,F,G,H
I,J,K,L
Replacing newlines with sed is a bit complicated (see this question). It is easier to use tr for the newlines. The rest can be done by sed.
The following command assumes that yourFile does not contain any ,.
tr '\n' , < yourFile | sed 's/,*$/\n/;s/,,/\n/g'
The tr part converts all newlines to ,. The resulting string will have no newlines.
s/,*$/\n/ removes trailing commas and appends a newline (text files usually end with a newline).
s/,,/\n/g replaces ,, by a newline. Two consecutive commas appear only where your original file contained two consecutive newlines, that is where the sections are separated by an empty line.

running a shell command that is quoted

In my Perl program I get to a point where I have a variable that has the following:
echo -e \"use\nseveral\nlines\"
I would like to run this command through the shell (using exec) as
echo -e "use\nseveral\nlines"
I tried eval on the variable before I passed it to exec, but that interpreted the \n and changed them to newlines.
EDIT:
Note that I am given the variable and do not have control over how it is input. Thus, given that the variable was input as such, is there a way to "unquote" it?
In Perl, you should use q{} or qq{} (or qx{} for execution) to avoid complicated quote escaping.
This should work for you (using q{} to avoid interpolating \n):
my $str = q{echo -e "use\nseveral\nlines"};
Now, you can execute it using qx:
qx{$str}
When you pass
echo -e \"use\nseveral\nlines\"
to the shell, it passes the following three args to the exec systems call:
echo
-e
use\nseveral\nlines
How does one create that last string? Here are a few ways:
"use\\nseveral\\nlines" # Escape \W chars in double-quoted strings.
'use\\nseveral\\nlines' # Escape \ and delimiters in single-quoted strings
'use\nseveral\nlines' # ...although it's optional if unambiguous.
The corresponding Perl command would be therefore be
exec('echo', '-e', 'use\nseveral\nlines');
system('echo', '-e', 'use\nseveral\nlines');
open(my $fh, '-|', 'echo', '-e', 'use\nseveral\nlines');
my #output = <$fh>;

Perl one-liner with single quote

I use a Perl one-liner to create an SQL statement, but I am not able to include single quotes.
This is what I want: Take the first field and add quotes to it.
echo "a,b" | perl -F',' -lane 'print $F[0];'
'a'
I tried a few different ways, but it didn't work for me.
 
echo "a,b" | perl -F',' -lane 'print qq('$F[0]');'
[0]
 
echo "a,b" | perl -F',' -lane 'print q('$F[0]');'
[0]
Here is another interesting issue.
It is printing a single quote with the print statement, but if I assign a value to the variable and print, it's not working.
perl -lwe "print q( i'am );"
i'am
perl -lwe "$b=q( didn't ); print $b"
How can we use single and double quotes in Perl one-liners?
You can't use single quotes alone. You need to escape them correctly using '\'' This works:
$ echo "a,b" | perl -F',' -lane 'print "'\''$F[0]'\''";'
'a'
You need to learn how your shell treats quotes.
I would just use the ASCII value for ':
echo "a,b" | perl -F',' -lane 'print "$F[0]\047";'
a'
q// and qq// operators can also be useful in one-liners.
Use a variable with the octal value:
echo "a,b" | perl -F',' -lane '$sq="\047"; print "$sq$F[0]$sq";'
Also, a slight modification of your attempt #1 would work:
echo "a,b" | perl -F',' -lane "print qq{'\$F[0]'};"
That uses double quotes for the outer set and escapes the dollar sign to prevent the shell from interpreting it.
Placing the script in double quotes rather than single quotes will allow you to use single quotes inside the script without having to escape or use ANSI sequences to represent the single quote. This is likely the most effective and easily-readable solution.

What am I doing wrong in this Perl one-liner?

I have a file that contains a lot of these
"/watch?v=VhsnHIUMQGM"
and I would like to output the letter code using a perl one-liner. So I try
perl -nle 'm/\"\/watch\?v=(.*?)\"/g' filename.txt
but it doesn't print anything.
What am I doing wrong?
The -n option processes each line but doesn't print anything out. So you need to add an explicit print if you successfully match.
perl -ne 'while ( m/\"\/watch\?v=(.+?)\"/g ) { print "$1\n" }' filename.txt
Another approach, if you're sure every line will match, is to use the -p option which prints out the value of $_ after processing, e.g.:
perl -pe 's/\"\/watch\?v=(.+?)\"/$1//' filename.txt
Your regex is fine. You're getting no output because the -n option won't print anything. It simply wraps a while (<>) { ... } loop around your program (run perl --help for brief explanations of the Perl options).
The following uses your regex, but add some printing. In list context, regexes with the /g option return all captures. Effectively, we print each capture.
perl -nle 'print for m/\"\/watch\?v=(.*?)\"/g' data.dat
You can split the string on "=" instead of matching:
perl -paF= -e '$_= #F[1]' filename.txt