How to expand variable literally when calling perl from csh script? - perl

Below is a csh script.
#! /bin/csh
set alpha=10\20\30;
set beta = $alpha.alpha;
perl -p -i.bak -e 's/gamma/'$beta'/' tmp;
The tmp file contains just the word gamma. After running tmp.csh, I expect 10\20\30.alpha in tmp, but it's now 102030.alpha.
How to preserve slashes in this situation?
Note: I wouldn't prefer changing definition of alpha variable, as it is used in the script else where where it needs to be in this format (10\20\30) only.
Thanks.

In csh, for your alpha assignment, the backslash is being taken to mean 'a literal 2 or 3'. In order to keep csh from doing this, the assignment needs to be enclosed in quotes.
#! /bin/csh
set alpha="10\20\30";
set beta = $alpha.alpha;
perl -p -i.bak -e 's/gamma/'$beta'/' tmp;
If in doubt, it's often helpful to 'echo' your variables out to see exactly what they contain. I don't understand your final note, as the 'alpha' variable is not equal to 10\20\30 the way you have it originally assigned.

Related

rename a string in a perl script eg name("this stays the same")

perl -pi-back -e 's/ACTUAL_WORD\(`SOMETHING`\)/EXPECTED_WORD\(`SOMETHING`\)/g;' \
inputfile.txt
I need the "something" to stay the same. something is like a variable that changes.
I think something like this should do it?
perl -pi-back -e 's/ACTUAL_WORD(.*)/EXPECTED_WORD($1)/g;' inputfile.txt
You capture the word in brackets and reuse it via $1 in the replacement. (You may need more brackets - it's unclear if additional are required based on your input).

Variable substitution in tcsh

I've gone through the manual for the tcsh but still can't figure out how it should work in my case or whether it should work at all. I basically need to extract part of the variable whose value is a six digit number. So I need to drop the first two characters and retrieve the last four.
The example below doesn't work (it would probably work in bash but tcsh HAS to be used):
set VAR1 = value1
set VAR2 = `echo ${VAR1:2}`
echo VAR2
It comes up with error Bad : modifier in $ (2), apparently because it's bash syntax and not understandable by tcsh, but can't figure out how to do it with tcsh arguments.
I'm not sure about using modifiers, but you can slice your string using cut or sed:
set VAR1=abcdef
"cut" characters 3-to-end
echo $VAR1 | cut -c3-
capture everything (\(.*\)), except for the first 2 characters (..)
echo $VAR1 | sed 's/..\(.*\)$/\1/'
You could also use the perl command line with regexs like the sed in #shx2 answer above:
echo $VAR1 | perl -pe 's/^\d\d(.*)/$1/'
Drops the first two digits it starts with.
Certainly tcsh doesn't accept bash-style modifiers, or vice versa. They're very different shells.
You say you need to extract the last 4 digits of a 6-digit number. I'll assume that number is a non-negative integer.
If you think of it as an arithmetic problem rather than as a string-processing problem, you can use the built-in # command:
% set VAR1 = 123456
% # VAR2 = $VAR1 % 10000
% echo VAR1=$VAR1 VAR2=$VAR2
VAR1=123456 VAR2=3456
%
(Why do you have to use tcsh? Obligatory link: http://www.perl.com/doc/FMTEYEWTK/versus/csh.whynot .)

Perl Variables in system()

I'm trying to access a series of webpages in perl and write them to a series of files. The code I have looks like this:
open IN , "AbsoluteFinalData.txt"; #Each line has a number and ID name separated by a tab.
while(my $line = <IN>){
chop $line; #removes newline at the end
my #first_split = split(/\t/, $line);
my $IDnum = $first_split[0];
my $Uniprot = $first_split[1];
system('Uniprot=$Uniprot; curl -o "$Uniprot.html" http://pfam.xfam.org/\protein/.$Uniprot'); #More stuff after
The program, however, is giving me fits when I try to call $Uniprot in system(). Is there any way to call a variable defined in the perl script using system()?
system('Uniprot=$Uniprot; curl -o "$Uniprot.html" http://pfam.xfam.org/\protein/.$Uniprot');
You use single quotes, which doesn't interpolate. The literal command:
Uniprot=$Uniprot; curl -o "$Uniprot.html" http://pfam.xfam.org/\protein/.$Uniprot
Is being executed.
You want to interpolate your variables, which means using double quotes (and escaping contained ones:)
system("Uniprot=$Uniprot; curl -o \"$Uniprot.html\" http://pfam.xfam.org/\protein/.$Uniprot");
Or the qq quote-like operator which functions like the double quote but avoids needing to escape contained double quotes:
system(qq(Uniprot=$Uniprot; curl -o "$Uniprot.html" http://pfam.xfam.org/\protein/.$Uniprot"));
Given that you're not relying on the system command to perform any shell interpretation or file I/O redirection, you can achieve everything you want safely like this:
system 'curl', '-o', "$Uniprot.html", "http://pfam.xfam.org/protein/.$Uniprot";
The "list" version of system is safer to use than the single string version because it prevents shell command injection attacks.
Note also the use of double quotes to enable Perl's own variable interpolation, and also that there's no need to create the shell local variable Uniprot=$Uniprot since it's not used by Curl and is only being used by you to attempt to perform variable interpolation yourself.
Perl only interpolates variables within double quotes ("..."), not single quotes ('...').
system("Uniprot=$Uniprot; curl -o \"$Uniprot.html\" http://pfam.xfam.org/\protein/.$Uniprot");
Will do the substitution you're looking for.

How to capture single quote when using Perl in CLi?

Suppose I have a text file with content like below:
'Jack', is a boy
'Jenny', is a girl
...
...
...
I'd like to use perl in Cli to only capture the names between pairs of single quotes
cat text| perl -ne 'print $1."\n" if/\'(\w+?)\'/'
Above command was what I ran but it didn't work. It seems like "'" messed up with Shell.
I know we have other options like writing a perl script. But given my circumstances, I'd like to find a way to fulfill this in Shell command line.
Please advise.
The shell has the interesting property of concatenating quoted strings. Or rather, '...' or "..." should not be considered strings, but modifiers for available escapes. The '...'-surrounded parts of a command have no escapes available. Outside of '...', a single quote can be passed as \'. Together with the concatenating property, we can embed a single quote like
$ perl -E'say "'\''";'
'
into the -e code. The first ' exits the no-escape zone, \' is our single quote, and ' re-enters the escapeless zone. What perl saw was
perl // argv[0]
-Esay "'"; // argv[1]
This would make your command
cat text| perl -ne 'print $1."\n" if/'\''(\w+?)'\''/'
(quotes don't need escaping in regexes), or
cat text| perl -ne "print \$1.qq(\n) if/'(\w+?)'/"
(using double quotes to surround the command, but using qq// for double quoted strings and escaping the $ sigil to avoid shell variable interpolation).
Here are some methods that do not require manually escaping the perl statement:
(Disclaimer: I'm not sure how robust these are – they haven't been tested extensively)
Cat-in-the-bag technique
perl -ne "$(cat)" text
You will be prompted for input. To terminate cat, press Ctrl-D.
One shortcoming of this: The perl statement is not reusable. This is addressed by the variation:
$pline=$(cat)
perl -ne "$pline" text
The bash builtin, read
Multiple lines:
read -rd'^[' pline
Single line:
read -r pline
Reads user input into the variable pline.
The meaning of the switches:
-r: stop read from interpreting backslashes (e.g. by default read interprets \w as w)
-d: determines what character ends the read command.
^[ is the character corresponding to Esc, you insert ^[ by pressing Ctrl-V then Esc.
Heredoc and script.
(You said no scripts, but this is quick and dirty, so might as well...)
cat << 'EOF' > scriptonite
print $1 . "\n" if /'(\w+)'/
EOF
then you simply
perl -n scriptonite text

Perl String Interpolation in Bash Command

I'm trying to use GNU Date to get the seconds between two dates. The reason I'm using GNU Date is for performance (in testing was 10x faster than Perl) for this purpose. However, one of my arguments is a perl variable. Like this:
my $b_row="2012-01-05 20:20:22";
my $exec =qx'CUR_DATE=`echo $(date +"%F %T")` ; echo $(($(date -d "$CUR_DATE" +%s)-$(date -d "$b_row" +%s)))';
The problem is that b_row is not being expanded. I've tried a couple different solutions (IPC::System::Simple) being one, tried adjusting the backticks etc. No success, any ideas how to do this appropriately? The main thing is I need to capture the output from the bash command.
Make it easier on yourself and do the minimum amount of work in the shell. This works for me:
my $b_row = '2012-01-05 20:20:22';
my $diff = qx(date -d "\$(date +'%F %T')" +%s) -
qx(date -d "$b_row" +%s);
Just be absolutely sure $b_row doesn't have any shell metacharacters in it.
That's because you use ' :
Using single-quote as a delimiter protects the command from
Perl's double-quote interpolation, passing it on to the shell
instead:
$perl_info = qx(ps $$); # that's Perl's $$
$shell_info = qx'ps $$'; # that's the new shell's $$
qx has the feature of letting you choose a convenient delimiter, including the option of whether to interpolate the string or not (by choosing ' as the delimiter). For this use case, sometimes you want interpolation and sometimes you don't, so qx (and backticks) may not be the right tool for the job.
readpipe is probably a better tool. Like the system EXPR command, it takes an arbitrary scalar as input, and you have all of Perl's tools at your disposal to construct that scalar. One way to do it is:
my $exec = readpipe
'CUR_DATE=`echo $(date +"%F %T")` ;' # interp not desired
. ' echo $(($(date -d "$CUR_DATE" +%s)-$(date -d "'
. qq/"$b_row"/ # now interp is desired
. ' +%s)))'; # interp not desired again