Is there a way to get Perl to avoid treating negative values as command-line switches? Neither stringifying nor backslashing the argument seems to help under Linux:
$ perl -e 'print "#ARGV\n";' 4 5
4 5
$ perl -e 'print "#ARGV\n";' -4 5
Unrecognized switch: -4 (-h will show valid options).
$ perl -e 'print "#ARGV\n";' "-4" 5
Unrecognized switch: -4 (-h will show valid options).
$ perl -e 'print "#ARGV\n";' '-4' 5
Unrecognized switch: -4 (-h will show valid options).
$ perl -e 'print "#ARGV\n";' \-4 5
Unrecognized switch: -4 (-h will show valid options).
$ perl -E "say join ', ', #ARGV" -- -1 2 3
-1, 2, 3
The trick is using the double-hyphen (--) to end the option parsing. Double-hyphen is a GNU convention:
$ touch -a
usage: touch [-acfm] [-r file] [-t [[CC]YY]MMDDhhmm[.SS]] file ...
$ touch -- -a
$ ls
-a
Related
I am trying to print out the text that is surrounded by single quotes.
/bin/bash -lc '/home/CASPER_REPORTS/scripts/CASPER_gen_report.sh CASPER_1'
/bin/bash -lc '/home/CASPER_REPORTS/scripts/CASPER_gen_report.sh CASPER_1A'
/bin/bash -lc '/home/CASPER_REPORTS/scripts/CASPER_gen_report.sh CASPER_2'
/bin/bash -lc '/home/CASPER_REPORTS/scripts/CASPER_gen_report.sh CASPER_3'
/bin/bash -lc '/home/CASPER_REPORTS/scripts/CASPER_gen_report.sh CASPER_3A'
The Boolean one I guess means that perl sees the string.
$ cat /tmp/casper_reports | perl -nle 'print /'.*'/'
1
1
1
1
1
However when I try and capture it with the parenthesis it throws an error
$ cat /tmp/boobomb | perl -nle 'print /'(.*)'/'
-bash: syntax error near unexpected token `('
In the Bash and Zsh shells, you can use $'' to allow escaped single quotes.
echo $'I wouldn\'t'
This also keeps $1 from being interpreted by bash and available to perl too.
perl -nle $'print $1 if /\'(.*)\'/' < /tmp/boobomb
also see
https://unix.stackexchange.com/questions/30903/how-to-escape-quotes-in-shell
Use hex for the single quote (27) via hexadecimal escape, so \x27
perl -wnE'say $1 if /\x27(.*)\x27/' input.txt
This assumes a single pair of single quotes, per shown sample data on which it was tested.
"ls" behaves differently when its output is being piped:
> ls ???
bar foo
> ls ??? | cat
bar
foo
How does it know, and how would I do this in Perl?
In Perl, the -t file test operator indicates whether a filehandle
(including STDIN) is connected to a terminal.
There is also the -p test operator to indicate whether a filehandle
is attached to a pipe.
$ perl -e 'printf "term:%d, pipe:%d\n", -t STDIN, -p STDIN'
term:1, pipe:0
$ perl -e 'printf "term:%d, pipe:%d\n", -t STDIN, -p STDIN' < /tmp/foo
term:0, pipe:0
$ echo foo | perl -e 'printf "term:%d, pipe:%d\n", -t STDIN, -p STDIN'
term:0, pipe:1
File test operator documentation at perldoc -f -X.
use IO::Interactive qw(is_interactive);
is_interactive() or warn "Being piped\n";
$ perl -MMIME::Base64 -e 'print encode_base64("syn_ack#163.com");'
c3luX2Fjay5jb20=
$ perl -MMIME::Base64 -e 'print decode_base64("c3luX2Fjay5jb20=");'
syn_ack.com
The encode result cannot decode to original string, why?
You have to escape # as \#or use different quotes.
This is because double quotes are expanded, and #163 is treated as an array #163 (even if this name is not valid identifier).
This works as expected:
perl -MMIME::Base64 -e "print encode_base64('syn_ack#163.com');"
c3luX2Fja0AxNjMuY29t
perl -MMIME::Base64 -e 'print encode_base64("syn_ack\#163.com");'
c3luX2Fja0AxNjMuY29t
perl -MMIME::Base64 -e "print decode_base64('c3luX2Fja0AxNjMuY29t');"
syn_ack#163.com
Switch the quotes. Perl will interpolate variables when using double-quotes.
$ perl -MMIME::Base64 -e "print encode_base64('syn_ack#163.com');"
c3luX2Fja0AxNjMuY29t
$ perl -MMIME::Base64 -e "print decode_base64('c3luX2Fja0AxNjMuY29t');"
syn_ack#163.com
http://perlmeme.org/howtos/using_perl/interpolation.html
When you see unexpected results with Perl, make sure warnings are enabled.
$ perl -w -MMIME::Base64 -e 'print encode_base64("syn_ack#163.com");'
Possible unintended interpolation of #163 in string at -e line 1.
c3luX2Fjay5jb20=
No interpolation occurs inside single-quoted ('') strings, so you could run
perl -w -MMIME::Base64 -e 'print encode_base64('syn_ack#163.com');'
or leave the double-quotes ("") and escape the #
perl -w -MMIME::Base64 -e 'print encode_base64("syn_ack\#163.com");'
Either outputs
c3luX2Fja0AxNjMuY29t
Decoding gives
$ perl -w -MMIME::Base64 -e 'print decode_base64("c3luX2Fja0AxNjMuY29t");'
syn_ack#163.com
Context: ActiveState Perl:
This is perl 5, version 12, subversion 4 (v5.12.4) built for MSWin32-x86-multi-thread
>perl -Mbignum=l -e "print 2 ** 32"
4294967296
>perl -Mbignum=l -e "print -2 ** 32"
-4294967296
Then I got to thinking, maybe I need to delimit the negative two.
>perl -Mbignum=l -e "print (-2) ** 32"
-2
Finally figured it out.
>perl -Mbignum=l -e "print ((-2) ** 32)"
4294967296
So how come all the parentheses?
This thread covers both of your questions (you have to go down a little to find the part corresponding to print (-2) ** 32).
Summarizing what is there:
For your first issue (perl -Mbignum=l -e "print -2 ** 32"): in Perl exponentiation has higher precedence than unary negation.
For the second issue (perl -Mbignum=l -e "print (-2) ** 32"): the key is the following warning in the documentation for print.
Also be careful not to follow the print keyword with a left parenthesis unless you want the corresponding right parenthesis to terminate the arguments to the print--interpose a + or put parentheses around all the arguments.
I don’t think this has to do with bignum.
$ perl -MO=Deparse -e "print 2 ** 32"
print 4294967296; # regular case
$ perl -MO=Deparse -e "print -2 ** 32"
print -4294967296; # ** has higher precedence than -
$ perl -MO=Deparse -e "print (-2) ** 32"
print(-2) ** 32; # parentheses parsed as function application
$ perl -MO=Deparse -e "print ((-2) ** 32)"
print 4294967296; # finally what you want
I guess the function application is what bit you (parsing print (-2) as the function print being called with -2 as an argument).
It's not a bignum related issue, if you try this:
perl -e "print (-2) + 32"
you get: -2
So the "problem" is with the arguments format of the print function
If you substitute your constants with variables, B::Deparse will show you how perl parses the code, so
$ perl -MO=Deparse,-p -e " print $fa ** $fb "
print(($fa ** $fb));
-e syntax OK
$ perl -MO=Deparse,-p -e " print -$fa ** $fb "
print((-($fa ** $fb)));
-e syntax OK
$ perl -MO=Deparse,-p -e " print (-$fa ) ** $fb "
(print((-$fa)) ** $fb);
-e syntax OK
$ perl -MO=Deparse,-p -e " print ( (-$fa ) ** $fb )"
print(((-$fa) ** $fb));
-e syntax OK
I have a script file which I need to modify with another script to insert a text at the 8th line.
String to insert: Project_Name=sowstest, into a file called start.
I tried to use awk and sed, but my command is getting garbled.
sed -i '8i This is Line 8' FILE
inserts at line 8
This is Line 8
into file FILE
-i does the modification directly to file FILE, no output to stdout, as mentioned in the comments by glenn jackman.
An ed answer
ed file << END
8i
Project_Name=sowstest
.
w
q
END
. on its own line ends input mode; w writes; q quits. GNU ed has a wq command to save and quit, but old ed's don't.
Further reading: https://gnu.org/software/ed/manual/ed_manual.html
OS X / macOS / FreeBSD sed
The -i flag works differently on macOS sed than in GNU sed.
Here's the way to use it on macOS / OS X:
sed -i '' '8i\
8 This is Line 8' FILE
See man 1 sed for more info.
the awk answer
awk -v n=8 -v s="Project_Name=sowstest" 'NR == n {print s} {print}' file > file.new
POSIX sed (and for example OS X's sed, the sed below) require i to be followed by a backslash and a newline. Also at least OS X's sed does not include a newline after the inserted text:
$ seq 3|gsed '2i1.5'
1
1.5
2
3
$ seq 3|sed '2i1.5'
sed: 1: "2i1.5": command i expects \ followed by text
$ seq 3|sed $'2i\\\n1.5'
1
1.52
3
$ seq 3|sed $'2i\\\n1.5\n'
1
1.5
2
3
To replace a line, you can use the c (change) or s (substitute) commands with a numeric address:
$ seq 3|sed $'2c\\\n1.5\n'
1
1.5
3
$ seq 3|gsed '2c1.5'
1
1.5
3
$ seq 3|sed '2s/.*/1.5/'
1
1.5
3
Alternatives using awk:
$ seq 3|awk 'NR==2{print 1.5}1'
1
1.5
2
3
$ seq 3|awk '{print NR==2?1.5:$0}'
1
1.5
3
awk interprets backslashes in variables passed with -v but not in variables passed using ENVIRON:
$ seq 3|awk -v v='a\ba' '{print NR==2?v:$0}'
1
a
3
$ seq 3|v='a\ba' awk '{print NR==2?ENVIRON["v"]:$0}'
1
a\ba
3
Both ENVIRON and -v are defined by POSIX.
sed -e '8iProject_Name=sowstest' -i start using GNU sed
Sample run:
[root#node23 ~]# for ((i=1; i<=10; i++)); do echo "Line #$i"; done > a_file
[root#node23 ~]# cat a_file
Line #1
Line #2
Line #3
Line #4
Line #5
Line #6
Line #7
Line #8
Line #9
Line #10
[root#node23 ~]# sed -e '3ixxx inserted line xxx' -i a_file
[root#node23 ~]# cat -An a_file
1 Line #1$
2 Line #2$
3 xxx inserted line xxx$
4 Line #3$
5 Line #4$
6 Line #5$
7 Line #6$
8 Line #7$
9 Line #8$
10 Line #9$
11 Line #10$
[root#node23 ~]#
[root#node23 ~]# sed -e '5ixxx (inserted) "line" xxx' -i a_file
[root#node23 ~]# cat -n a_file
1 Line #1
2 Line #2
3 xxx inserted line xxx
4 Line #3
5 xxx (inserted) "line" xxx
6 Line #4
7 Line #5
8 Line #6
9 Line #7
10 Line #8
11 Line #9
12 Line #10
[root#node23 ~]#
Perl solutions:
quick and dirty:
perl -lpe 'print "Project_Name=sowstest" if $. == 8' file
-l strips newlines and adds them back in, eliminating the need for "\n"
-p loops over the input file, printing every line
-e executes the code in single quotes
$. is the line number
equivalent to #glenn's awk solution, using named arguments:
perl -slpe 'print $s if $. == $n' -- -n=8 -s="Project_Name=sowstest" file
-s enables a rudimentary argument parser
-- prevents -n and -s from being parsed by the standard perl argument parser
positional command arguments:
perl -lpe 'BEGIN{$n=shift; $s=shift}; print $s if $. == $n' 8 "Project_Name=sowstest" file
environment variables:
setenv n 8 ; setenv s "Project_Name=sowstest"
echo $n ; echo $s
perl -slpe 'print $ENV{s} if $. == $ENV{n}' file
ENV is the hash which contains all environment variables
Getopt to parse arguments into hash %o:
perl -MGetopt::Std -lpe 'BEGIN{getopt("ns",\%o)}; print $o{s} if $. == $o{n}' -- -n 8 -s "Project_Name=sowstest" file
Getopt::Long and longer option names
perl -MGetopt::Long -lpe 'BEGIN{GetOptions(\%o,"line=i","string=s")}; print $o{string} if $. == $o{line}' -- --line 8 --string "Project_Name=sowstest" file
Getopt is the recommended standard-library solution.
This may be overkill for one-line perl scripts, but it can be done
For those who are on SunOS which is non-GNU, the following code will help:
sed '1i\^J
line to add' test.dat > tmp.dat
^J is inserted with ^V+^J
Add the newline after '1i.
\ MUST be the last character of the line.
The second part of the command must be in a second line.
sed -i "" -e $'4 a\\\n''Project_Name=sowstest' start
This line works fine in macOS
macOS sed solutions
for example: inserts at line 1
ns
# recommended 👍
# This command only needs to write one line
$ sed -i '' '1s/^/The new First Line\n/' ./your-source-file-name
ni
# not recommended 👎
# This way the command needs to be written on multiple lines
$ sed -i '' '1i\
The new First Line\
' ./your-source-file-name
test demo
$ sed -i '' '1s/^/Perl 🐪 camel\n/' ./multi-line-text.txt
it is working fine in linux to add in 2 lines.
sed '2s/$/ myalias/' file
Thank you umläute
sed -i "" -e $'4 a\\\n''Project_Name=sowstest' filename
the following was usefull on macOS to be able to add a new line after the 4
In order to loop i created an array of folders, ti iterate on them in mac zsh
for foldercc in $foldernames;
sed -i "" -e $'4 a\\\n''Project_Name=sowstest' $foldercc/filenames;