Dan said:
$ perldoc -q cut #no help, so asking here.
How do I emulate UNIX's "cut -c18-31,49-51"?
Larry said:
perl -lne 'print substr($_, 17, 24). substr($_, 48 , 3)'
which also works if typo of 24 is corrected to 14,
and works fine with either . or , between substrs
Dr.Ruud said:
s/^.{17}(.{14}).{17}(.{1,3})/$1$2/
|| s/^.{17}(.{1,14})/$1/;
which doesnt work because extra characters at end of line
arent removed (note z at end)
% perl -le 'print +(a..z)x2'|\
perl -lpe 's/^.{17}(.{14}).{17}(.{1,3})/$1$2/ || s/^.{17}(.{1,14})/$1/;'
rstuvwxyzabcdewxyz
This method can be done with alternation inside the regexp
and fixed for junk at end of line
% perl -le 'print +(a..z) x 2;' |\
perl -lpe 's/^.{17}(.{1,14})(.{17}(.{1,3}))*.*/$1$3/ '
rstuvwxyzabcdewxy
But it still silently fails to match the cut command
on short lines, and I dont know how to fix it correctly for
both short and medium length lines.
% perl -le 'print +(a..c)x2'| cut -c18-31,49-51 # short (blank)
% perl -le 'print +(a..j) x 2;' | cut -c18-31,49-51 # medium
hij
% perl -le 'print +(a..j)x2'|\
perl -lpe 's/^(.{17}(.{1,14})(.{17}(.{1,3})))*.*/$2$4/ ' # fails med
% perl -le 'print +(a..c)x2'|\
perl -lpe 's/^.{17}(.{1,14})(.{17}(.{1,3}))*.*/$1$3/ ' # fails sh
abcabc
$ perl -le 'print +(a..z) x 2;' |\
perl -F'//' -lane '$[ = 1; print
@F[18..31,49..51];'
rstuvwxyzabcdewxy
FWIW, from perldoc perlvar:
As of release 5 of Perl, assignment to "$[" is
treated as a compiler directive, and cannot
influence the behavior of any other file.
Its use is highly discouraged.
and previously suggested using split, with prepended blank.
% perl -le 'print +(a..z) x 2;' |\
perl -lne 'print join "", (split "", " $_")[18..31,49..51]'
rstuvwxyzabcdewxy
These golfing variations also work
perl -lne 'print ((split "", " $_")[18..31,49..51])' # no join
perl -lpe '$_= join "",(split "", " $_")[18..31,49..51]' # no print
We can unshift an irrelevant array element to correct the column count,
instead of using the deprecated directive $[.
% perl -le 'print +(a..z) x 2;' |\
perl -F'//' -lane 'unshift @F,0;print @F[18..31,49..51];'
rstuvwxyzabcdewxy
(e-mail address removed)-berlin.de (Anno Siegel) suggested unpack
with template of '@17a14 @48a3' which has same args
as method substr($_, 17,14),substr($_,48,3)'
% perl -le 'print +(a..z) x 2;' |\
perl -lne 'print @x=unpack q(@17a14@48a3),$_'
rstuvwxyzabcdewxy
But it fails noisily (instead of quietly)
to match the cut command on short lines.
% perl -le 'print +(a..c) x 2;' |\
perl -lne 'print @x=unpack q(@17a14@48a3),$_'
@ outside of string at -e line 1, <> line 1.
I tried removing strings instead of keeping them,
but it has argument conversion, and fails on short lines.
% perl -le 'print +(a..z) x 2;' |\
perl -lpe 'substr($_,51)="";substr($_,31,-3)="";substr($_,0,17)=""';
rstuvwxyzabcdewxy
**********
To summarize, these two split [slice] versions
have the most similar commandlines to 'cut -c18-31,49-51'
for productivity of one-liner-ing, and linelength compatibility.
perl -lne 'print ((split "", " $_")[18..31,49..51])'
perl -F'//' -lane 'unshift @F,0;print @F[18..31,49..51];'
Dan Jacobson's original comment:
Certainly there is no one-liner possible unless I express the ranges
differently?
The above solutions are one-liner possiblities for sufficiently loose
definitions of un-different range expressions. s/-/../g;
For exactly the same range expression 18-31,49-51
this might work, but I cant quite get it under one line of 80 chars.
Eval on random user inputs are also unsafe, so dont allow that,
but it was a good learning experience.
% perl -e 'print +(a..z) x 2,"\n",+(A..Z) x 2,"\n";' |\
perl -lne 'BEGIN{($c=shift)=~s/-/../g;eval q:sub p{print((split""," $_")[:.$c."])}"}p' 18-31,49-51
rstuvwxyzabcdewxy
RSTUVWXYZABCDEWXY