LD_PRELOAD - shell expansion trouble - linux

I want to call an executable with LD_PRELOAD-ing some .so files.
But I have a problem, that bash shell expansion seems not to work in variable expansion:
These are the files, I'm trying to set for LD_PRELOAD:
nuclear#Korhal:~$ ls speedhack/speedhack*.so
speedhack/speedhack32.so speedhack/speedhack.so
But the shell expansion does not work here:
nuclear#Korhal:~$ LD_PRELOAD="speedhack/speedhack*.so" ./my_executable
ERROR: ld.so: object 'speedhack/speedhack*.so' from LD_PRELOAD cannot be preloaded: ignored.
I wrote a script to check, what happens with shell variables expansion:
nuclear#Korhal:~$ cat test.sh
#!/bin/bash
echo "LD_PRELOAD w/o quotes:"
echo $LD_PRELOAD
echo "LD_PRELOAD with quotes:"
echo "$LD_PRELOAD"
Running it:
nuclear#Korhal:~$ LD_PRELOAD="speedhack/speedhack*.so" ./test.sh
ERROR: ld.so: object 'speedhack/speedhack*.so' from LD_PRELOAD cannot be preloaded: ignored.
LD_PRELOAD w/o quotes:
speedhack/speedhack32.so speedhack/speedhack.so
LD_PRELOAD with quotes:
speedhack/speedhack*.so
But it gets more complicated: the star is not expanded when variable is accessed from ld.so
and inside the script it gets expanded only if I have no quotes in echo.. As I know, double quotes make no difference in variable expansion (echo $VAR should be the same as echo "$VAR". But echo '$VAR' will print the string $VAR)
The main question: How to force shell expansion in variable assignment, so that LD_PRELOAD holds the list of files?

You can put the filenames in an array and use that to expand the list:
FILES=(speedhack/speedhack*.so)
LD_PRELOAD="${FILES[#]}" ./my_executable

You can also use backquotes or $() syntax to copy-paste the results of a command onto a command-line. Then use double quotes to grab all the words into the same variable.
LD_PRELOAD="$(ls speedhack/speedhack*.so)" ./my_executable

Related

pass string with spaces to gcc [duplicate]

This question already has answers here:
How can I store a command in a variable in a shell script?
(12 answers)
Closed 4 years ago.
These work as advertised:
grep -ir 'hello world' .
grep -ir hello\ world .
These don't:
argumentString1="-ir 'hello world'"
argumentString2="-ir hello\\ world"
grep $argumentString1 .
grep $argumentString2 .
Despite 'hello world' being enclosed by quotes in the second example, grep interprets 'hello (and hello\) as one argument and world' (and world) as another, which means that, in this case, 'hello will be the search pattern and world' will be the search path.
Again, this only happens when the arguments are expanded from the argumentString variables. grep properly interprets 'hello world' (and hello\ world) as a single argument in the first example.
Can anyone explain why this is? Is there a proper way to expand a string variable that will preserve the syntax of each character such that it is correctly interpreted by shell commands?
Why
When the string is expanded, it is split into words, but it is not re-evaluated to find special characters such as quotes or dollar signs or ... This is the way the shell has 'always' behaved, since the Bourne shell back in 1978 or thereabouts.
Fix
In bash, use an array to hold the arguments:
argumentArray=(-ir 'hello world')
grep "${argumentArray[#]}" .
Or, if brave/foolhardy, use eval:
argumentString="-ir 'hello world'"
eval "grep $argumentString ."
On the other hand, discretion is often the better part of valour, and working with eval is a place where discretion is better than bravery. If you are not completely in control of the string that is eval'd (if there's any user input in the command string that has not been rigorously validated), then you are opening yourself to potentially serious problems.
Note that the sequence of expansions for Bash is described in Shell Expansions in the GNU Bash manual. Note in particular sections 3.5.3 Shell Parameter Expansion, 3.5.7 Word Splitting, and 3.5.9 Quote Removal.
When you put quote characters into variables, they just become plain literals (see http://mywiki.wooledge.org/BashFAQ/050; thanks #tripleee for pointing out this link)
Instead, try using an array to pass your arguments:
argumentString=(-ir 'hello world')
grep "${argumentString[#]}" .
In looking at this and related questions, I'm surprised that no one brought up using an explicit subshell. For bash, and other modern shells, you can execute a command line explicitly. In bash, it requires the -c option.
argumentString="-ir 'hello world'"
bash -c "grep $argumentString ."
Works exactly as original questioner desired. There are two restrictions to this technique:
You can only use single quotes within the command or argument strings.
Only exported environment variables will be available to the command
Also, this technique handles redirection and piping, and other shellisms work as well. You also can use bash internal commands as well as any other command that works at the command line, because you are essentially asking a subshell bash to interpret it directly as a command line. Here's a more complex example, a somewhat gratuitously complex ls -l variant.
cmd="prefix=`pwd` && ls | xargs -n 1 echo \'In $prefix:\'"
bash -c "$cmd"
I have built command processors both this way and with parameter arrays. Generally, this way is much easier to write and debug, and it's trivial to echo the command you are executing. OTOH, param arrays work nicely when you really do have abstract arrays of parameters, as opposed to just wanting a simple command variant.

How to store command arguments which contain double quotes in an array?

I have a Bash script which generates, stores and modifies values in an array. These values are later used as arguments for a command.
For a MCVE I thought of an arbitrary command bash -c 'echo 0="$0" ; echo 1="$1"' which explains my problem. I will call my command with two arguments -option1=withoutspace and -option2="with space". So it would look like this
> bash -c 'echo 0="$0" ; echo 1="$1"' -option1=withoutspace -option2="with space"
if the call to the command would be typed directly into the shell. It prints
0=-option1=withoutspace
1=-option2=with space
In my Bash script, the arguments are part of an array. However
#!/bin/bash
ARGUMENTS=()
ARGUMENTS+=('-option1=withoutspace')
ARGUMENTS+=('-option2="with space"')
bash -c 'echo 0="$0" ; echo 1="$1"' "${ARGUMENTS[#]}"
prints
0=-option1=withoutspace
1=-option2="with space"
which still shows the double quotes (because they are interpreted literally?). What works is
#!/bin/bash
ARGUMENTS=()
ARGUMENTS+=('-option1=withoutspace')
ARGUMENTS+=('-option2=with space')
bash -c 'echo 0="$0" ; echo 1="$1"' "${ARGUMENTS[#]}"
which prints again
0=-option1=withoutspace
1=-option2=with space
What do I have to change to make ARGUMENTS+=('-option2="with space"') work as well as ARGUMENTS+=('-option2=with space')?
(Maybe it's even entirely wrong to store arguments for a command in an array? I'm open for suggestions.)
Get rid of the single quotes. Write the options exactly as you would on the command line.
ARGUMENTS+=(-option1=withoutspace)
ARGUMENTS+=(-option2="with space")
Note that this is exactly equivalent to your second option:
ARGUMENTS+=('-option1=withoutspace')
ARGUMENTS+=('-option2=with space')
-option2="with space" and '-option2=with space' both evaluate to the same string. They're two ways of writing the same thing.
(Maybe it's even entirely wrong to store arguments for a command in an array? I'm open for suggestions.)
It's the exact right thing to do. Arrays are perfect for this. Using a flat string would be a mistake.

'less' the file specified by the output of 'which'

command 'which' shows the link to a command.
command 'less' open the file.
How can I 'less' the file as the output of 'which'?
I don't want to use two commands like below to do it.
=>which script
/file/to/script/fiel
=>less /file/to/script/fiel
This is a use case for command substitution:
less -- "$(which commandname)"
That said, if your shell is bash, consider using type -P instead, which (unlike the external command which) is built into the shell:
less -- "$(type -P commandname)"
Note the quotes: These are important for reliable operation. Without them, the command may not work correctly if the filename contains characters inside IFS (by default, whitespace) or can be evaluated as a glob expression.
The double dashes are likewise there for correctness: Any argument after them is treated as positional (as per POSIX Utility Syntax Guidelines), so even if a filename starting with a dash were to be returned (however unlikely this may be), it ensures that less treats that as a filename rather than as the beginning of a sequence of options or flags.
You may also wish to consider honoring the user's pager selection via the environment variable $PAGER, and using type without -P to look for aliases, shell functions and builtins:
cmdsource() {
local sourcefile
if sourcefile="$(type -P -- "$1")"; then
"${PAGER:-less}" -- "$sourcefile"
else
echo "Unable to find source for $1" >&2
echo "...checking for a shell builtin:" >&2
type -- "$1"
fi
}
This defines a function you can run:
cmdsource commandname
You should be able to just pipe it over, try this:
which script | less

Shell script for setting environment variable

I am writing a shell script to set the environment variables whose values are available in a file. Below is the shell script I wrote,
VARIABLE_FILE=env-var.dat
if [ -f ${VARIABLE_FILE} ] ; then
. ${VARIABLE_FILE}
if [ ! -z "${TEST_VAR1}" ] ; then
export TEST_VAR1="${TEST_VAR1}"
fi
if [ ! -z "${TEST_VAR2}" ] ; then
export TEST_VAR2="${TEST_VAR2}"
fi
fi
The above code works only in bash shell, since I have used export command to set the environment variable and it fails if I used it with any other shell. Is there is any command to set the environment variable which works in any shell ?
"Fancier" shells like bash and zsh permit you to set a variable and export it as an environment variable at the same time like so:
export FOO=bar
With a standard POSIX bourne shell, the equivalent is achieved by doing it in two commands:
FOO=bar
export FOO
Note that once you've exported a variable, you can reset it to a different value later in the script and it's still exported (you don't need to export it again). Also, you can export several variables at a time:
FOO=bar
BAZ=quux
export FOO BAZ
You mentioned tcsh in your comment, but csh and derivatives are completely different from bourne-based shells (and not recommended for use!). You can rarely make a shell script compatible with both sh and csh at the same time. For csh, look into setenv
If you really want this to happen, it can be done, but it's tricky. One way to do it is to use awk to output the correct syntax and evaluate the text coming back from awk. To share a single environment variable value file between major sh and csh flavors, the following command in a file will import a variable value file to the environment: (yes, yes, it's one huge line, due to the inflexible way that some shells treat the backticks. If you didn't mind having a .awk file too, you could use awk -f...)
eval `awk '{ var = $1; $1=""; val=substr($0,2); if ( ENVIRON["SHELL"] ~ /csh$/) { print "setenv", var, " \"" val "\";" } else { print var "=\"" val "\"" ; print "export", var }}' $HOME/env_value_file`
The variable value file is in this format:
FOO value for foo
BAR foo bar
BAZ $BAR plus values $FOO
Design notes for educational purposes:
In awk, there's no easy way of accessing fields 2-NF, so if there
could be spaces in our variable values we need to modify $1 to get
$0 to be close to get the value we want.
To get this to work, since a SHELL variable is always set, but not as an
environment variable and not with a consistent capitalization, you have to wet
a SHELL environment variable from the shell's value as below.
as an environment variable before you use the script.
Also, if you want the new environment values to be present after the import
environment script you need to source the environment script.
If a shell doesn't do eval well, you'll have to tweak the script.
For bourne shell flavors (bash, sh, ksh, zsh):
export SHELL
. import_environment
For csh flavors: (shell variable tends to be lower case in csh shells)
setenv SHELL "$shell"
source import_environment

Declaring User Defined Variable in Shell Scripting (csh shell)

I am trying to learn shell scripting and trying to create a user defined variable within the script, first:
howdy="Hello $USER !"
echo $howdy
However, when I execute the script (./first) I get this:
howdy=Hello aaron!: Command not found.
howdy: Undefined variable.
What am I doing wrong?
You have two errors in you code:
you are using sh syntax instead of csh one to set the variable
you are not escaping the "!" character (history substitution)
Try this:
#!/bin/csh
set howdy="Hello $USER \!"
echo $howdy
csh expects that you set variables. Try
set howdy="Hello $USER"
echo $howdy
You are doing
howdy=''Hello $USER !''
You need to enclose the string in double quotes as:
howdy="Hello $USER !"
You seem to be using two single quotes in place of a double quote.

Resources