Pipe echo of change the current directory to sh does not work [duplicate] - linux

This question already has answers here:
Why can't I change directories using "cd" in a script?
(33 answers)
Closed 4 years ago.
If I do the code:
echo "printf 'working'" | sh
the code prints out working
but when I want to change the current directory this way:
echo "cd ../" | sh
the current directory isn't changed.
Do you know the reason behind that behavior?
do you know how to echo cd command to sh in a working way?

echo "cd /" | sh
actually creates 2 new processes: echo, and sh. The sh process most probably does change the directory, but then just exits. You could test this by
echo "cd ../; touch Jimmix_was_here" | sh
ls -l ../Jimmix_was_here
which should show empty file Jimmix_was_here file, with current timestamp (if you had write permission to the parent directory; otherwise the first command would throw error.)
There's no way to change current directory of a process from within a child; after all if it was possible, it would be a security hole!
Note: this reminds me of a seemingly paradoxical fact: why /bin/cd exists?
Note 2: Try pstree | cat and find both pstree and cat--they are siblings!

Related

Bash - Dump script works when running manually but not by crontab [duplicate]

This question already has answers here:
Difference between sh and Bash
(11 answers)
Closed 3 months ago.
For security purposes, I have been writing a script that daily create dump, and manage them for some daily dumps, and some monthly dumps. I have a weird issue that I can clearly understand. When I run the script above manually ./save_db.sh evrything works. But I did add this in crontab of the same user (which is not root).
cd /home/debian/script && sh save_db.sh
The script is this:
#!/bin/bash
nom_dump="$(date +%H:%M-%d%m%y)"
mysqldump -u debian --all-databases | gzip -c > /home/debian/bdd_dump/journalier/"$nom_dump".sql.gz
sauv_mensuelle="$(date +%d)"
if [ $sauv_mensuelle == "01" ]
then
cp "$nom_dump".sql.gz /home/debian/bdd_dump/mensuel
compte_sauv="$(ls /home/debian/bdd_dump/mensuel | wc -l)"
if (( $compte_sauv > 6 ))
then
clean_mensuel="$(ls -t /home/debian/bdd_dump/mensuel/ | tail -1)"
rm /home/debian/bdd_dump/mensuel/"$clean_mensuel"
fi
fi
compte_semaine="$(ls /home/debian/bdd_dump/journalier/ | wc -l)"
if (( $compte_semaine > 7 ))
then
clean_daily="$(ls -t /home/debian/bdd_dump/journalier/ | tail -1)"
rm /home/debian/bdd_dump/journalier/"$clean_daily"
fi
When crontab runs it, the last part is not working as intended, but I can't know why. Dumps older than 7 days are not being removed
Thanks :)
The way you are calling this script:
cd /home/debian/script && sh save_db.sh
Is environment dependent. Chmod +x your script and replace the call in crontab with
/path/to/script/my-script-name.sh
You only need one shell process to run, not two, and you don't need to change your working directory to invoke it.

Passing (and executing) a quoted command pipeline as a function argument [duplicate]

This question already has answers here:
How to run script commands from variables?
(3 answers)
Execute command in a variable don't execute the latter part of a pipe
(1 answer)
Closed 1 year ago.
Today I encountered something quite strange
I have two scripts:
wrong.sh:
execute()
{
${#}
}
execute "ls -l | less"
right.sh:
execute()
{
${#}
}
execute ls -l | less
Running sh wrong.sh gives the following output:
ls: less: No such file or directory
ls: |: No such file or directory
While running sh right.sh gives me the same thing as running ls -l | less
May I know:
(1) While running sh wrong.sh will give me that wrong output
(2) How to modify the execute function so that running sh wrong.sh will gives me the same thing as running ls -l | less
In your wrong.sh invocation, you are trying to run a command with the rather unusual 12 byte long name ls -l | less (which might actually exist, say as /usr/bin/'ls -l | less')
If you want to interpret a string as a shell command, the easiest thing to do is:
sh -c "$command_as_string"
So in your case
execute()
{
sh -c "$1" # no ${#} if you only intend to ever pass a single string
# or $SHELL -c "$1" if you want the *same* shell as you're calling execute() from
}

ssh and execute several commands as another user through a heredoc [duplicate]

This question already has answers here:
Usage of expect command within a heredoc
(1 answer)
Pass commands as input to another command (su, ssh, sh, etc)
(3 answers)
Closed 4 years ago.
I have a script that I need to execute through ssh as another use, is there a way to pass whole script like this:
ssh -t user#server.com sudo -u user2 sh -c << EOF
cd /home
ls
dir=$(pwd)
echo "$dir"
echo "hello"
....
EOF
Returns: sh: -c: option requires an argument
ssh'ing and sudo'ing separately is not an option and putting .sh file directly on the machine is not possible.
sh -c requires a command string as the argument. Since you are reading the commands from standard input (through heredoc), you need to use sh -s option:
ssh -t user#server.com sudo -u user2 sh -s << 'EOF'
cd /home
ls
dir=$(pwd)
echo "$dir"
echo "hello"
...
EOF
From man sh:
-c
string If the -c option is present, then commands are read from string. If there are arguments after the string, they are assigned to the positional parameters, starting with $0.
-s
If the -s option is present, or if no arguments remain after option processing, then commands are read from the standard input. This option allows the positional parameters to be set when invoking an interactive shell.
Need to quote the heredoc marker to prevent the parent shell from interpreting the content.

How to execute commands read from the txt file using shell? [duplicate]

This question already has answers here:
Run bash commands from txt file
(4 answers)
Closed 4 years ago.
I tried to execute commands read it from txt file. But only 1st command is executing, after that script is terminated. My script file name is shellEx.sh is follows:
echo "pwd" > temp.txt
echo "ls" >> temp.txt
exec < temp.txt
while read line
do
exec $line
done
echo "printed"
if I keep echo in the place of exec, just it prints both pwd and ls. But i want to execute pwd and ls one by one.
o/p am getting is:
$ bash shellEx.sh
/c/Users/Aditya Gudipati/Desktop
But after pwd, ls also need to execute for me.
Anyone can please give better solution for this?
exec in bash is meant in the Unix sense where it means "stop running this program and start running another instead". This is why your script exits.
If you want to execute line as a shell command, you can use:
line="find . | wc -l"
eval "$line"
($line by itself will not allow using pipes, quotes, expansions or other shell syntax)
To execute the entire file including multiline commands, use one of:
source ./myfile # keep variables, allow exiting script
bash myfile # discard variables, limit exit to myfile
A file with one valid command per line is itself a shell script. Just use the . command to execute it in the current shell.
$ echo "pwd" > temp.txt
$ echo "ls" >> temp.txt
$ . temp.txt

Bash cat on the last created remote file [duplicate]

This question already has answers here:
Shell Script error: "head: invalid trailing option -- 1"
(3 answers)
Closed 8 years ago.
I am trying to recover the content of the last created file on a remote server.
When connected to the remote server I do this:
cat `ls -t /mypath/*.csv | head -1`
CMD="cat `ls -t /mypath/*.txt | head -1`"
But when I try to use the same command:
ssh#XX.XX.XX.XX $CMD
I get an error: ls cannot access /mypath/*.csv No such file or directory.
The ` is forcing to execute the ls on the local system on not the remote.
Is there another way?
Thank you
Your command is failing is because the backticks in $CMD are expanded locally when you create the variable, rather than being expanded on the remote side. So ssh#XX.XX.XX.XX $CMD is actually going to look something like ssh#XX.XX.XX.XX "cat /mypath/local_file" (and local_file may not exists on the remote host, and is probably not the file you want).
You can prevent this local expansion by providing the command directly to ssh.
ssh user#host 'cat /mypath/$(ls -t /mypath/*.txt | head -1)'
ls returns the pathname relative to the directory so you will also need to include the path of the base directory /mypath/ in your cat invocation. To avoid this hardcoding pass the -d flag to ls.
ssh user#host 'cat $(ls -dt /mypath/*.txt | head -1)'

Resources