What does ${1+"$#"} mean in a unix shell script? [duplicate] - sh

This question already has answers here:
${1:+"$#"} in /bin/sh
(4 answers)
Closed 7 years ago.
I came across this construct while reviewing some older unix shell scripts, what does it mean, and why is is used?
${1+"$#"}

I found this explanation from unix haters handbook, page 152 of text (page 190 of the pdf). http://web.mit.edu/~simsong/www/ugh.pdf
It’s the way to exactly reproduce the command line arguments in the
/bin/sh family of shells shell script.
It says, “If there is at least one argument ( ${1+ ), then substitute in
all the arguments ( “$#” ) preserving all the spaces, etc. within each
argument.
If we used only “$#” then that would substitute to “” (a null argument)
if there were no invocation arguments, but we want no arguments reproduced in
that case, not “”.
Why not “$*” etc.? From a sh(1) man page:
Inside a pair of double quote marks (“”), parameter and
command substitution occurs and the shell quotes the results to
avoid blank interpretation and file name generation. If $* is
within a pair of double quotes, the positional parameters are
substituted and quoted, separated by quoted spaces (“$1
$2 …”); however, if $# is within a pair of double quotes, the
positional parameters are substituted and quoted, separated by
unquoted spaces (“$1” “$2” …).
I think ${1+“$#”} is portable all the way back to “Version 7 Unix.”
Wow! All the way back to Version 7.

Google should be your best friend! This is a similar questions asked almost 2 years ago.
https://unix.stackexchange.com/questions/68484/what-does-1-mean-in-a-shell-script-and-how-does-it-differ-from

Related

Unable to move forward using cd

I'm having a problem moving forward through a path with PowerShell. I am able to move up the directory but not down. Here's the situation:
I open PowerShell and type in the "pwd" command and it shows that I am currently in PS C:\Users\Robert Inspiron14>
I type the command "cd.." and now I move to PS C:\Users>
I then attempt to change directories by typing: "cd C:\Users\Robert Inspiron14" and I am unable to. Unfortunately, I can't post a picture yet due to lack of reputation.
I'm able to perform the change in CMD but not PowerShell. Also, I don't know how to change the User from "Robert Inspiron14" to just "Robert". Any help is appreciated!
Before PowerShell can execute your cd command, it needs to parse it, and PowerShell's parser interprets your command like this:
cd C:\Users\Robert Inspiron14
\/ \_____________/ \________/
Command Name | |
argument 1 |
argument 2
In other words, C:\Users\Robert and Inspiron14 are interpreted as separate arguments.
Neither argument is a path to a valid directory, so cd (or rather Set-Location for which cd is an alias) throws an error.
You can force PowerShell to recognize C:\Users\Robert Inspiron14 as a single string argument by qualifying its boundaries using quotation marks (both " and ' will work):
cd 'C:\Users\Robert Inspiron14'
You can read more about how PowerShell parses command expressions in the about_Parsing help topic
To complement Mathias R. Jessen's helpful answer with more background information:
Quoting an argument that contains spaces is a general syntactic necessity, in all shells, because unquoted spaces are used to separate multiple arguments.
It isn't only spaces that require quoting, but any of PowerShell's so-called metacharacters (characters that, when used unquoted, have syntactic function); for instance, passing the path to a directory literally named a;b requires quoting as well, given that ; would otherwise be interpreted as a statement separator.
There are multiple quoting styles:
Since your path value is a literal - it contains no variable references or expressions - a verbatim (single-quoted) string ('...') is the best choice.
cd 'C:\Users\Robert Inspiron14'
If your path contains variables or subexpressions, you must use an expandable (double-quoted) string ("...")[1]
cd "$HOME\Documents"
Another, less common solution is to individually escape the space characters with `, the so-called backtick, PowerShell's escape character:
cd C:\Users\Robert` Inspiron14
Also note:
PowerShell's tab-completion automatically applies quoting as necessary.
cd.. is the name of a built-in function in PowerShell, whose sole purpose is to emulate cmd.exe's (questionably permissive) behavior (see below); the function performs a syntactically correct Set-Location .. call (verify by executing ${function:cd..}), with a space separating the command name from its argument.
Contrast with cmd.exe:
Unfortunately, cmd.exe's built-in cd command decided not to enforce its usual syntax rules, and enabled calls such as cd C:\Program Files.
It should never have done that: While convenient at first glance, it constitutes a problematic exception from the usual rules that invites confusion.
Note that cmd.exe's tab completion properly quotes arguments that contain spaces.
Similarly, cd.. was unfortunately allowed as as syntactically exceptional alternative to the correct cd .. - see the comments on this answer for details.
[1] Note "..."-quoting isn't strictly necessary if you use variable references in a path, as long as any literal components do not require quoting; e.g., $HOME\foo is fine without quoting, whereas the " around "$HOME\foo bar" are required. With subexpressions ($(...)), the rules get more complicated, so the simplest approach is to always use "..."-quoting with them.

Perl: Bareword found where operator expected at [duplicate]

This question already has answers here:
Why is the escape of quotes lost in this regex substitution?
(2 answers)
Closed 4 years ago.
I am trying to use perl to replace a string like so:
perl -pe "s/FlDigVal/$DIGN/" Header.xml > $DDIRP/$FNAME.xml
If DIGN=+q4T/h/B8Saf0im3LtBevNvMPsd1PRG5Tz+Iq/uwjXA= i get the following syntax error:
Having no space between pattern and following word is deprecated at -e line 1.
Bareword found where operator expected at -e line 1, near "s/FlDigVal/+q4T/h"
Having no space between pattern and following word is deprecated at -e line 1.
syntax error at -e line 1, near "s/FlDigVal/+q4T/h"
Execution of -e aborted due to compilation errors.
I guess this is related to /hbeing in variable DIGN. Is there a way to escape those reserved words?
Don't use shell variables, which are just non-hygienic macros. Export the variable to Perl's environment:
DIGN=$DIGN perl -pe 's/FlDigVal/$ENV{DIGN}/'
Note the single quotes: we don't want the shell to change the Perl commands.
or pass the value as an argument:
perl -pe 'BEGIN { $replace = shift } s/FlDigVal/$replace/' "$DIGN" Header.xml
Nevertheless, you seem to be editing an XML document with regular expressions. It's a painful way, there are libraries like XML::LibXML that handle XML correctly. E.g. what would happen if DIGN contained & or <?
The problem is not with reserved words but /.
If DIGN contains +q4T/h/B8Saf0im3LtBevNvMPsd1PRG5Tz+Iq/uwjXA=, your command passes the following code to perl:
s/FlDigVal/+q4T/h/B8Saf0im3LtBevNvMPsd1PRG5Tz+Iq/uwjXA=/
Here s/FlDigVal/+q4T/ parses as a substitution command, but the rest is garbage.
The solution is to not let the shell interpolate variables into code. Instead you can pass strings via the environment:
DIGN="$DIGN" perl -pe 's/FlDigVal/$ENV{DIGN}/' Header.xml
(If DIGN is already exported, you don't need the DIGN="$DIGN" part.)
Here we use single quotes (no shell interpolation) and let perl grab a value from the environment.

Perl Subroutine Declaration Unexpected Symbols [duplicate]

This question already has answers here:
What do dollar, at-sign and semicolon characters in Perl parameter lists mean?
(2 answers)
Closed 5 years ago.
I have been looking at some Perl code that has some subroutine declarations that make no sense to me. They appear as:
foo($$$$;$);
foo(\$\$\$);
What do the symbols ";" and "\" do or mean in these declarations?
They're "prototypes" - but Perl prototypes aren't like those in other languages, and probably shouldn't be used.
The $ denotes a scalar argument to the function. ;$ denotes an optional additional scalar argument. And \$ denotes a reference argument.
See also: Why are Perl 5's function prototypes bad?

Perl specific code

The following program is in Perl.
cat "test... test... test..." | perl -e '$??s:;s:s;;$?::s;;=]=>%-{<-|}<&|`{;;y; -/:-#[-`{-};`-{/" -;;s;;$_;see'
Can somebody help me to understand how it works?
This bit of code's already been asked about on the Debian forums.
According to Lacek, the moderator on that thread, what the code originally did is rm -rf /, though they mention they've changed the version there so that people trying to figure out how it works don't delete their entire filesystem. There's also an explanation there of what the various parts of the Perl code do.
(Did you post this knowing what it did, or were you unaware of it?)
To quote Lacek's post on it:
Anyway, here is how the script works.
It is basically two regex substitutions and one transliteration.
Piping anything into its standard input makes no difference, the perl
code doesn't use its input in any way. If you split the long line on
the boundaries of the expressions, you get this:
$??s:;s:s;;$?::
s;;=]=>%-{\\>%<-{;;
y; -/:-#[-`{-};`-{/" -;;
s;;$_;see
The first line is a condition which does nothing save makes the code
look more difficult. If the previous command originated from the perl
code wasn't successful, it does some substitutions on the standard
input (which the program doesn't use, so effectively it substitutes
the nothing). Since no previous command exists, $? is always 0, so the
first line never gets executed.
The second line substitutes the
standard input (the nothing) for seemingly meaningless garbage.
The third line is a transliteration operator. It defines 4 ranges, in
which the characters gets substituted to the one range and the 4
characters given in the transliteration replacement. I'd prefer not to
write the whole transliteration table here, because it's a bit long.
If you are really interested, just write the characters in the defined
ranges (space to '/', ':' to '#', '[' to '(backtick)', and '{' to '}'), and
write next to them the characters from the replacement range ('(backtick)' to
'{'), and finally, write the remaining characters (/,", space and -)
from the replacement pattern. When you have this table, you can see
what character gets replaced to what.
The last line executes the
resulting command by substituting the nothing with the resulted string
(which is 'xterm'. Originally it was 'system"rm -rf /"', and is held
in $_), evaluates the substitution as an expression and executes it.
(I've substituted 'backtick' for the actual backtick character here so that the code auto-formatting doesn't kick in.)

What is the Perl equivalent to the shell's "$#"?

I'm working on a Perl script that I was hoping to capture a string entered on the command line without having to enter the quotes (similiar to bash's "$#" ability). I'll be using this command quite a bit so I was hoping that this is possible. If I have:
if ($ARGV) {
I have to put the command line string in quotes. I'd rather do the command something like this:
htmlencode some & HTML <> entities
Without the quotes. Is there a way to do this in Perl?
The #ARGV array contains the arguments to the Perl script - no quotes needed.
That said, the question asks about:
I have to put the command line string in quotes. I'd rather do the command something like this:
htmlencode some & HTML <> entities
Without the quotes. Is there a way to do this in perl?
Well, if the command shown is written at the shell command line, you have to obey shell conventions - which means escaping the '&' and '<>' to prevent the shell from interpreting them. Likewise, within a Perl script, that sequence would need to be protected from Perl. Maybe you'd write:
system "htmlencode", "some", "&", "HTML", "<>", "entities";
That is, everything would have to be in quotes - but that notation would avoid executing the shell and having the shell interpret the commands.
Alternatively again, if you put the arguments into an array (with quotes at the time the array is loaded), then you could pass that array to system and not use any quotes:
my #args = ( "htmlencode", "some", "&", "HTML", "<>", "entities" );
system #args;
But I think the question is confused.
You put quotes around $# in bash so that it expands to have each element in the array quoted. The reason to do that is so that each element of the array continues to be treated as a single argument when you pass them all to the next command.
The analogue to that in Perl is when you want to pass those parameters to another external command. If you're running the external program with the backtick operators, then you'd need to quote each parameter, but if you use system, then Perl will take care of keeping all the parameters separate for you.
In fact, separate parameters are the way programs are executed on Unix anyway. The single-string command-line format is there because we need to be able to type things at the command prompt. Like all shells, bash has special rules about how to split that single string into multiple arguments. But if you already have them separated in a Perl array, don't put them back into a single string. Keep them separate.