For example,
create a bash tmp.sh script with the following,
export tmp=abc
read _test
echo "$_test"
Execute bash tmp.sh
Input '$tmp/def'.
Expected result: 'abc/def'
Actual result: '$tmp/def'
check this
eval "echo $_test"
or
bash -c "echo $_test"
Edit Latter (bash -c) uses sub-shell which is safe in comparison with eval
You can use the envsubst command to substitute environment variables like this:
echo "$_test" | envsubst
or, since this is in bash:
envsubst <<<"$_test"
This is significantly safer than either eval or bash -c, since it won't do anything other than replacing instances of $var or ${var} with the corresponding variable values.
Related
I am trying to dynamically create alias' from the output of another command line tool.
For example:
> MyScript
blender="/opt/apps/blender/blender/2.79/blender"
someOtherAlias="ls -l"
I am trying the following code:
MyScript | {
while IFS= read -r line;
do
`echo alias $line`;
done;
}
But when I run this, I get the following error:
bash: alias: -l": not found
Just trying to run this command by itself gives me the same error:
> `echo 'alias someOtherAlias="ls -l"'`
bash: alias: -l": not found
But obviously the following command does work:
alias someOtherAlias="ls -l"
I've tried to find someone else who may have done this before, but none of my searches have come up with anything.
I would appreciate any and all help. Thanks!
See how bash (and posix shells) command parsing and quoting works and see difference between syntax and literal argument: for example '.."..' "..'.." are litteral quotes in an argument whereas " or ' are shell syntax and are not part of argument
also, enabling tacing with set -x may help to understand :
set -x
`echo 'alias someOtherAlias="ls -l"'`
++ echo 'alias someOtherAlias="ls -l"'
+ alias 'someOtherAlias="ls' '-l"'
bash: alias: -l": not found
bash sees 3 words : alias, someOtherAlias="ls and -l".
and alias loops over its arguments if they contain a = it create an alias otherwise it displays what alias argument is as -l" is not an alias it shows the error.
Note also as backquotes means command is run in a subshell (can be seen with mutiple + in trace) it will have no effect in current shell.
eval may be use to reinterpret literal as bash syntax (or to parse again a string).
So following should work, but be careful using eval on arbitrary arguments (from user input) can run arbitrary command.
eval 'alias someOtherAlias="ls -l"'
Finally also as bash commands after pipe are also run in subshell.
while IFS= read -r line;
do
`echo alias $line`;
done <MyScript
In my program I need to know the maximum number of process I can run. So I write a script. It works when I run it in shell but but when in program using system("./limit.sh"). I work in bash.
Here is my code:
#/bin/bash
LIMIT=\`ulimit -u\`
ACTIVE=\`ps -u | wc -l \`
echo $LIMIT > limit.txt
echo $ACTIVE >> limit.txt
Anyone can help?
Why The Original Fails
Command substitution syntax doesn't work if escaped. When you run:
LIMIT=\`ulimit -u\`
...what you're doing is running a command named
-u`
...with the environment variable named LIMIT containing the value
`ulimit
...and unless you actually have a command that starts with -u and contains a backtick in its name, this can be expected to fail.
This is because using backticks makes characters which would otherwise be syntax into literals, and running a command with one or more var=value pairs preceding it treats those pairs as variables to export in the environment for the duration of that single command.
Doing It Better
#!/bin/bash
limit=$(ulimit -u)
active=$(ps -u | wc -l)
printf '%s\n' "$limit" "$active" >limit.txt
Leave off the backticks.
Use modern $() command substitution syntax.
Avoid multiple redirections.
Avoid all-caps names for your own variables (these names are used for variables with meaning to the OS or system; lowercase names are reserved for application use).
Doing It Right
#!/bin/bash
exec >limit.txt # open limit.txt as output for the rest of the script
ulimit -u # run ulimit -u, inheriting that FD for output
ps -u | wc -l # run your pipeline, likewise with output to the existing FD
You have a typo on the very first line: #/bin/bash should be #!/bin/bash - this is often known as a "shebang" line, for "hash" (#) + "bang" (!)
Without that syntax written correctly, the script is run through the system's default shell, which will see that line as just a comment.
As pointed out in comments, that also means only the standardised options available to the builtin ulimit command, which doesn't include -u.
I have two shell script files test1.sh and test2.sh . I have another file called translogs.txt.
Now I need to copy the values of two variables in test1.sh to translog.txt and the same variables need to be copied to the corresponding values in test2.sh.
test1.sh
#!/bin/sh
ONE="000012"
TIME="2013-02-19 15:31:06"
echo -e "$ONE\n$TIME">translog.txt;
translog.txt
ONE="000012"
TIME="2013-02-19 15:31:06"
But here in test2.sh, I want the same value as in translog.txt to the corresponding variable
test2.sh
#!/bin/sh
ONE="000012"
TIME="2013-02-19 15:31:06"
1 Diry solution
$> cat translog.txt
ONE="000012"
TIME="2013-02-19 15:31:06"
With perl regular expression grep could match these value using lookbehind operator.
$> grep --only-matching --perl-regex "(?<=ONE\=).*" translog.txt
"000012"
And for TIME:
$> grep --only-matching --perl-regex "(?<=TIME\=).*" translog.txt
"2013-02-19 15:31:06"
So from withing the test2.sh script you can use it like this:
#!/bin/bash
ONE=`grep --only-matching --perl-regex "(?<=ONE\=).*" translog.txt`
TIME=`grep --only-matching --perl-regex "(?<=TIME\=).*" translog.txt`
2 Command line solution
Another solution pointed out in one of the links below would be to use:
the source (a.k.a. .) command to load all of the variables in the file into the current shell:
$ source translog.txt
Now you have access to the values of the variables defined inside the file:
$ echo $TIME
"2013-02-19 15:31:06"
3 Easiest solution
Another approach was mentioned by #user2086768. Put these lines to `test2.sh:
#!/bin/bash
eval $(cat translog.txt)
And as a result you would have assigned the two variables within the test2.sh script:
ONE="000012"
TIME="2013-02-19 15:31:06"
you can easily check that adding:
echo $ONE
echo $TIME
Check also these links:
how to get value of variable config in bash?
Here's a more general overview: Loading data into bash variables
As translog.txt is valid bash code, you could do:
source translog.txt
in test2.sh, ONE and TWO would be available in test2.sh.
A word of warning, that does open you up to shell script injection attacks if the values for ONE and TWO were to come from an untrusted source.
If your translog.txt is as you say, then this will work
#!/bin/sh
while read aa
do
eval "$aa"
done < translog.txt
eval should work for you.
Try to use this version of test2.sh:
test2.sh
#!/bin/bash
eval $(cat translog.txt)
echo $ONE
echo $TIME
This outputs:
000012
2013-02-19 15:31:06
I wrote a simple shell script to get the version of Perl modules installed
on a server and I keep receiving the following error:
Can't find string terminator "'" anywhere before EOF at -e line 1.
Here is my script:
#!/bin/sh
#
mod_name="Sub::Uplevel"
tmp1="perl -M$mod_name -e 'print \"\$$mod_name::VERSION\"'"
echo $tmp1
$tmp1
If I just directly run the echo'd line (perl -MSub::Uplevel -e 'print "$Sub::Uplevel::VERSION"'), it works. Why doesn't the line work when its run from the variable $tmp1?
In place of just $tmp1, eval works:
eval "$tmp1"
That's because splitting a variable into words (for arguments) is done strictly by splitting on $IFS, not the normal input-parsing. eval forces the normal input parsing.
How did I figure this out?
Change your tmp1= line to put an echo in front, and you get:
perl -MSub::Uplevel -e 'print "$Sub::Uplevel::VERSION"'
Note that the ' are still there, which you wouldn't expect. If you write a quick script:
#!/bin/sh
for a in "$#"; do
echo "arg: $a"
done
and put a call to that in place of echo, you find how the arguments are really split:
arg: perl
arg: -MSub::Uplevel
arg: -e
arg: 'print
arg: "$Sub::Uplevel::VERSION"'
So, you can see that's splitting on spaces, so IFS.
It's always better to construct commands using bash arrays. That will keep arguments with whitespace properly grouped:
#!/bin/bash
mod_name="Sub::Uplevel"
perl_script=$(printf 'print "$%s::VERSION"' $mod_name)
tmp1=(perl -M$mod_name -e "$perl_script")
echo "${tmp1[#]}"
output=$( "${tmp1[#]}" )
Arrays are a bash feature, so the shebang line must reference bash not sh.
I'd usually write what you are doing with backticks, to run the command inside the shell:
#!/bin/sh
#
mod_name="Sub::Uplevel"
tmp1=`perl -M$mod_name -e 'print \"\$$mod_name::VERSION\"'`
echo $tmp1
Then you can work on $tmp1 as needed. It also avoids dealing with escaping.
Try to execute the script the below way(debugging the script):
sh -vx your_script.sh
Then you would be able to see where exactly the problem is.
I donot have the shell to execute it right now.
I would like to set a variable which name is stored in a file (is an output of a sed executed earlier)
the file would look like:
py1
so setting our variable would be like: set cat file=value
but echoing $py1 gives me nothing.
Is that possible with bash version 2.05?
This is the "preferred" way to do it (bash) without using cat or eval
declare $(<file)=value
Use eval:
eval "$(cat file)=value"
Update: The command substitution $(cat file) can be replaced by the equivalent but faster $(< file).
You'll need to use eval
eval $(cat file)=value