Bash: How to perform redirection coming from variable expansion - bash

I am trying to run a command with a variable which holds another command that suppresses warning messages of the jar. However, it is not working as expected and I can't figure out what I am doing wrong.
TEST=${TEST:-2> /dev/null}
java -jar ~/bin/aw.jar ${Test}

Redirection is performed only if it is unquoted and present in the command line literally rather than originating from any kind of expansion:
$ ls
$ echo hello >out
$ ls
out
$ cat out
hello
$ rm *
$ echo hello '>out'
hello >out
$ ls
$ x='>out'
$ echo hello $x
hello >out
$ ls
In order to interpret redirection operator coming from a quoted string or an expansion you must execute the command through eval (note, however, that this may result in undesired expansions in other parts of your command):
$ ls
$ x='>out'
$ eval echo hello $x
$ ls
out
$ cat out
hello

Related

Is there a way to print interpolated shell commands while preserving redirections?

Suppose I have the following shell program.
#!/bin/sh
FOO="foo"
echo $FOO | cat
I want to generate another shell program that does the same thing as this one, except that all shell variables have been substituted. For example,
#!/bin/sh
echo "foo" | cat
I know that I can get close if I run the above program using #!/bin/sh -x, but that output does not preserve redirections. Instead, I get
+ FOO=foo
+ echo foo
+ cat
foo
Any ideas?
The following shell:
$ cat eval.sh
echo "#!/bin/sh"
FOO="foo"
echo "echo $FOO | cat"
will write a shell:
$ sh eval.sh
#!/bin/sh
echo foo | cat
which does what you need.

From within a shell script I want to access input given from other command

I am messing around with shell scripts and I am trying to get my script to take input from another command, like say ls. It is called like this:
ls | ./example.sh
I try to access the input from within example.sh
#!/bin/bash
echo $1
but it echoes back nothing. Is there another way to reference parameters given to bash by other commands, because it works if I type in:
./example.sh poo
Parameters and input aren't the same thing:
$ ls
example.sh foo
$
$ cat example.sh
for param; do
printf 'argument 1: "%s"\n' "$param"
done
while IFS= read -t 1 -r input; do
printf 'input line: "%s"\n' "$input"
done
$
$ ls | ./example.sh
input line: "example.sh"
input line: "foo"
$
$ ls | ./example.sh bar
argument 1: "bar"
input line: "example.sh"
input line: "foo"
$
$ ./example.sh $(ls)
argument 1: "example.sh"
argument 1: "foo"
Parameters are the arguments passed to the script to start it running, input is what the script reads while it's running.
You can use xargs to do that.
ls | xargs ./example.sh
Resource
https://ss64.com/bash/xargs.html
https://www.cyberciti.biz/faq/linux-unix-bsd-xargs-construct-argument-lists-utility/
Or read man page for it

Strange behaviour for echo with -e flag passed to bash with -c flag

I cannot understand the behaviour of this bash script (which I cut it out of a longer real use case):
# This is test.sh
cmd="echo -e \"\n\n\n\t===== Hello World =====\n\n\""
sh -c "$cmd"
What it prints is:
$ ./test.sh
-e
===== Hello World =====
$
If I remove the -e flag, everything is printed correctly, with quoted chars correctly interpreted and without the '-e' spoil: but it shouldn't be like that.
My bash is: GNU bash, version 3.2.57(1)-release (x86_64-apple-darwin17), under macOS.
In Posix mode (when run as sh), bash 3.2's echo command takes no options; -e is just another argument to write to standard output. Compare:
$ bash -c 'echo -e "a\tb"'
a b
$ sh -c 'echo -e "a\tb"'
-e a b
A literal tab is printed in both cases because Posix echo behaves the same as bash echo -e.
For this reason, printf is almost always better to use than echo to provide consistent behavior.
cmd='printf "\n\n\n\t===== Hello World =====\n\n"'
sh -c "$cmd"
sh-4.2# cat test.sh
cmd="echo -e \"\n\n\n\t===== Hello World =====\n\n\""
sh -c "$cmd"
sh-4.2# ./test.sh
===== Hello World =====
sh-4.2#
It is getting printed correctly on my machine
OK, I think I found it myself, from here:
sh, the Bourne shell, is old. Its behaviour is specified by the POSIX standard. If you want new behaviour, you use bash, the Bourne Again shell, which gets new features added to it all the time. On many systems, sh is just bash, and bash turns on a compatibility mode when run under that name.
Groan...

How to pass argument in bash pipe from terminal

i have a bash script show below in a file called test.sh
#!/usr/bin/env bash
echo $1
echo "execution done"
when i execute this script using
Case-1
./test.sh "started"
started
execution done
showing properly
Case-2
If i execute with
bash test.sh "started"
i'm getting the out put as
started
execution done
But i would like to execute this using a cat or wget command with arguments
For example like.
Q1
cat test.sh |bash
Or using a command
Q2
wget -qO - "url contain bash" |bash
So in Q1 and Q2 how do i pass argument
Something simlar to this shown in this github
https://github.com/creationix/nvm
Please refer installation script
$ bash <(curl -Ls url_contains_bash_script) arg1 arg2
Explanation:
$ echo -e 'echo "$1"\necho "done"' >test.sh
$ cat test.sh
echo "$1"
echo "done"
$ bash <(cat test.sh) "hello"
hello
done
$ bash <(echo -e 'echo "$1"\necho "done"') "hello"
hello
done
You don't need to pipe to bash; bash runs as standard in your terminal.
If I have a script and I have to use cat, this is what I'll do:
cat script.sh > file.sh; chmod 755 file.sh; ./file.sh arg1 arg2 arg3
script.sh is the source script. You can replace that call with anything you want.
This has security implications though; just running an arbitrary code in your shell - especially with wget where the code comes from a remote location.

Shell sourced file output piped vs redirected has different effects.

I'm struggling to understand the difference between | and > operators
I've looked in places like:
https://www.gnu.org/software/bash/manual/html_node/Redirections.html
and
Pipe vs redirect into process
But can't make enough sense of the explanations.
Here is my practical example:
test-a.sh:
alias testa='echo "alias testa here"'
echo "testa echo"
echo "testa echo2"
test-b.sh:
alias testb='echo "alias testb here"'
echo "testa echo"
echo "testa echo2"
test-pipes.sh:
function indent() {
input=$(cat)
echo "$input" | perl -p -e 's/(.*)/ \1/'
}
source test-a.sh | indent
testa
source test-b.sh > >(indent)
testb
output:
$ source test-pipes.sh
testa echo
testa echo2
test-pipes.sh:10: command not found: testa
testa echo
testa echo2
alias testb here
Piping doesn't allow the alias to be set in the current process, but the redirection does.
Can someone give a simple explanation?
From the bash man page:
Each command in a pipeline is executed as a separate process (i.e., in a sub‐shell).
Many things child processes do are isolated from the parent. Among the list are: changing the current directory, setting shell variables, setting environment variables, and aliases.
$ alias foo='echo bar' | :
$ foo
foo: command not found
$ foo=bar | :; echo $foo
$ export foo=bar | :; echo $foo
$ cd / | :; $ pwd
/home/jkugelman
Notice how none of the changes took effect. You can see the same thing with explicit subshells:
$ (alias foo='echo bar')
$ foo
foo: command not found
$ (foo=bar); echo $foo
$ (export foo=bar); echo $foo
$ (cd /); pwd
/home/jkugelman
Redirections, on the other hand, do not create subshells. They merely change where the input and output of a command go. The same goes with function calls. Functions are executed in the current shell, no subshell, so they're able to create aliases.

Resources