What's the difference between -> result=$(ls -l) (or) -> result=`ls -l` - linux

I'v run both commands and they both seem to do the same thing, is this true or is there something happening I'm not seeing?
These two appear to do the same thing:
result=$(ls -l)
result=`ls -l`

Please check out
http://nixcraft.com/shell-scripting/13288-loop-input-backticks-vs-differences.html#post19804
for a nice explanation of this case.

The bash man page says the following, under Command Substitution.
When the old-style backquote form of
substitution is used, backslash
retains its literal meaning except
when followed by $, `, or . The
first backquote not preceded by a
backslash terminates the command
sub-stitution. When using the
$(command) form, all characters
between the parentheses make up the
command; none are treated specially.
Command substitutions may be nested.
To nest when using the backquoted
form, escape the inner backquotes with
backslashes.
Online copies of the sh man page:
bash
osx
free bsd

Related

Egrep results are current command and grabage

Im trying to egrep lines that contain nothing but a single occurrence of "Hihihihihihihi!", with arbitrarily many 'hi's
Here is what I write
egrep "^Hi(hi)*!$" myfile.txt
But it didn't work. After pressing enter, the command was displayed again:
egrep "^Hi(hi)*myfile.txt" mayflies.txt
Anyone can help me?
Thanks!
The shell is interpreting !$ to substitute the last argument of the previous commend.
To disable these shell substitutions, change the double quotes to single quotes.
egrep '^Hi(hi)*!$' myfile.txt
Alternatively, you can use the -x switch to match only whole lines, obviating the need for the ^ and $ characters, and thus avoiding the fatal !$ argument substitution:
egrep -x "Hi(hi)*!" myfile.txt
You don't say what shell, but I suspect the problem you have is that the exclamation mark (!) is extra special to the shell. You need to escape that:
egrep "^Hi(hi)*\!$" myfile.txt
Should work in most shells where that's true.
Changing the double quotes to single quotes is not enough for all shells, the exclamation is still special inside single quotes. I just tested all this in the tcsh, other shells will have differences.
try it with single quotes. I think the $ is being interpreted by BASH as something, not sure what:
egrep '^Hi(hi)*!$' myfile.txt

Unix 'alias' fails with 'awk' command

I'm creating an alias in Unix and have found that the following command fails..
alias logspace='find /apps/ /opt/ -type f -size +100M -exec ls -lh {} \; | awk '{print $5, $9 }''
I get the following :
awk: cmd. line:1: {print
awk: cmd. line:1: ^ unexpected newline or end of string
Any ideas on why the piped awk command fails...
Thanks,
Shaun.
To complement's #Dropout's helpful answer:
tl;dr
The problem is the OP's attempt to use ' inside a '-enclosed (single-quoted) string.
The most robust solution in this case is to replace each interior ' with '\'' (sic):
alias logspace='find /apps/ /opt/ -type f -size +100M -exec ls -lh {} \; |
awk '\''{print $5, $9 }'\'''
Bourne-like (POSIX-compatible) shells do not support using ' chars inside single-quoted ('...'-enclosed) strings AT ALL - not even with escaping.
(By contrast, you CAN escape " inside a double-quoted string as \", and, as in #Droput's answer, you can directly, embed ' chars. there, but see below for pitfalls.)
The solution above effectively builds the string from multiple, single-quoted strings into which literal ' chars. - escaped outside the single-quoted strings as \' - are spliced in.
Another way of putting it, as #Etan Reisinger has done in a comment: '\'' means: "close string", "escape single quote", "start new string".
When defining an alias, you usually want single quotes around its definition so as to delay evaluation of the command until the alias is invoked.
Other solutions and their pitfalls:
The following discusses alternative solutions, based on the following alias:
alias foo='echo A '\''*'\'' is born at $(date)'
Note how the * is effectively enclosed in single quotes - using above technique - so as to prevent pathname expansion when the alias is invoked later.
When invoked, this alias prints literal A * star is born, followed by the then-current date and time, e.g.: A * is born at Mon Jun 16 11:33:19 EDT 2014.
Use a feature called ANSI C quoting with shells that support it: bash, ksh, zsh
ANSI C-quoted strings, which are enclosed in $'...', DO allow escaping embedded ' chars. as \':
alias foo=$'echo A \'*\' is born at $(date)'
Pitfalls:
This feature is not part of POSIX.
By design, escape sequences such as \n, \t, ... are interpreted, too (in fact, that's the purpose of the feature).
Use of alternating quoting styles, as in #Dropout's answer:
Pitfall:
'...' and "..." have different semantics, so substituting one for the other can have unintended side-effects:
alias foo="echo A '*' is born at $(date)" # DOES NOT WORK AS INTENDED
While syntactically correct, this will NOT work as intended, because the use of double quotes causes the shell to expand the command substitution $(date) right away, and thus hardwires the date and time at the time of the alias definition into the alias.
As stated: When defining an alias, you usually want single quotes around its definition so as to delay evaluation of the command until the alias is invoked.
Finally, a caveat:
The tricky thing in a Bourne-like shell environment is that embedding ' inside a single-quoted string sometimes - falsely - APPEARS to work (instead of generating a syntax error, as in the question), when it instead does something different:
alias foo='echo a '*' is born at $(date)' # DOES NOT WORK AS EXPECTED.
This definition is accepted (no syntax error), but won't work as expected - the right-hand side of the definition is effectively parsed as 3 strings - 'echo a ', *, and ' is born at $(date)', which, due to how the shell parses string (merging adjacent strings, quote removal), results in the following, single, literal string: a * is born at $(date). Since the * is unquoted in the resulting alias definition, it will expand to a list of all file/directory names in the current directory (pathname expansion) when the alias is invoked.
You chould use different quotes for surrounding the whole text and for inner strings.
Try changing it to
alias logspace="find /apps/ /opt/ -type f -size +100M -exec ls -lh {} \; | awk '{print $5, $9 }'"
In other words, your outer quotes should be different than the inner ones, so they don't mix.
Community wiki update:
The redeeming feature of this answer is recognizing that the OP's problem lies in unescaped use of the string delimiters (') inside a string.
However, this answer contains general string-handling truths, but does NOT apply to (Bourne-like, POSIX-compatible) shell programming specifically, and thus does not address the OP's problem directly - see the comments.
Note: Code snippets are meant to be pseudo code, not shell language.
Basic strings: You canNOT use the same quote within the string as the entire string is delimited with:
foo='hello, 'world!'';
^--start string
^-- end string
^^^^^^^^--- unknown "garbage" causing syntax error.
You have to escape the internal strings:
foo='hello, \'world!\'';
^--escape
This is true of pretty much EVERY programming language on the planet. If they don't provide escaping mechanisms, such as \, then you have to use alternate means, e.g.
quotechar=chr(39); // single quote is ascii #39
foo='hello ,' & quotechar & 'world!' & quotechar;
Escape the $ sign not the awk's single quotes and use double quotes for the alias.
Try this :
alias logspace="find /apps/ /opt/ -type f -size +100M -exec ls -lh {} \; | awk '{print \$5, \$9 }'"

How to echo "$x_$y" in Bash script?

It is very interesting that if you intend to display 0_1 with Bash using the code
x=0
y=1
echo "$x_$y"
then it will only display
1
I tried echo "$x\_$y" and it doesn't work.
How can I echo the form $x_$y? I'm going to use it on a file name string.
Because variable names are allowed to have underscores in them, the command:
echo "$x_$y"
is trying to echo ${x_} (which is probably empty in your case) followed by ${y}. The reason for this is because parameter expansion is a greedy operation - it will take as many legal characters as possible after the $ to form a variable name.
The relevant part of the bash manpage states:
The $ character introduces parameter expansion, command substitution, or arithmetic expansion.
The parameter name or symbol to be expanded may be enclosed in braces, which are optional but serve to protect the variable to be expanded from characters immediately following it which could be interpreted as part of the name.
When braces are used, the matching ending brace is the first } not escaped by a backslash or within a quoted string, and not within an embedded arithmetic expansion, command substitution, or parameter expansion.
Hence, the solution is to ensure that the _ is not treated as part of the first variable, which can be done with:
echo "${x}_${y}"
I tend to do all my bash variables like this, even standalone ones like:
echo "${x}"
since it's more explicit, and I've been bitten so many times in the past :-)
This way:
$ echo "${x}_${y}"
0_1
wrap it in curly braces:
echo "${x}_${y}"
Just to buck the trend, you can also do this:
echo $x'_'$y
You can have quoted and unquoted parts next to each other with no space between. And since ' isn't a legal character for a variable name, bash will substitute only $x. :)

shell quoting behavior

I just learned that, quoting make a huge difference in some cases, and I did something to test it, here is what I just did,
$ xfs=$(find . -type f -perm -111) #find all files with x-perm
$ echo "$xfs"
./b.out
./a.out
$ echo $xfs
./b.out ./a.out #why all in one line, but the above takes two?
If $xfs contains \n, AFAIK, echo -e will expand \n, but how can echo "$xfs" take 2 lines?
Any whitespace is normally in shell considered to be an argument separator. Thus, your first example has two arguments. echo prints the arguments separated by one space, and that's the behaviour you see in your second example.
However, when you use quotes, anything between them is one argument, and it is printed literally. The one argument in your first example already contains a newline, so it is printed with a newline.
The -e option from the bash echo builtin regulates the expansion of escape sequences like \n; however, you don't have any escape sequences. The variable contains a literal newline.

Escape file name for use in sed substitution

How can I fix this:
abc="a/b/c"; echo porc | sed -r "s/^/$abc/"
sed: -e expression #1, char 7: unknown option to `s'
The substitution of variable $abc is done correctly, but the problem is that $abc contains slashes, which confuse sed. Can I somehow escape these slashes?
Note that sed(1) allows you to use different characters for your s/// delimiters:
$ abc="a/b/c"
$ echo porc | sed -r "s|^|$abc|"
a/b/cporc
$
Of course, if you go this route, you need to make sure that the delimiters you choose aren't used elsewhere in your input.
The GNU manual for sed states that "The / characters may be uniformly replaced by any other single character within any given s command."
Therefore, just use another character instead of /, for example ::
abc="a/b/c"; echo porc | sed -r "s:^:$abc:"
Do not use a character that can be found in your input. We can use : above, since we know that the input (a/b/c/) doesn't contain :.
Be careful of character-escaping.
If using "", Bash will interpret some characters specially, e.g. ` (used for inline execution), ! (used for accessing Bash history), $ (used for accessing variables).
If using '', Bash will take all characters literally, even $.
The two approaches can be combined, depending on whether you need escaping or not, e.g.:
abc="a/b/c"; echo porc | sed 's!^!'"$abc"'!'
You don't have to use / as pattern and replace separator, as others already told you. I'd go with : as it is rather rarely used in paths (it's a separator in PATH environment variable). Stick to one and use shell built-in string replace features to make it bullet-proof, e.g. ${abc//:/\\:} (which means replace all : occurrences with \: in ${abc}) in case of : being the separator.
$ abc="a/b/c"; echo porc | sed -r "s:^:${abc//:/\\:}:"
a/b/cporc
backslash:
abc='a\/b\/c'
space filling....
As for the escaping part of the question I had the same issue and resolved with a double sed that can possibly be optimized.
escaped_abc=$(echo $abc | sed "s/\//\\\AAA\//g" | sed "s/AAA//g")
The triple A is used because otherwise the forward slash following its escaping backslash is never placed in the output, no matter how many backslashes you put in front of it.

Resources