Shell script change directory with variable - linux

I know this question has been asked numerous times, but I still could not find any good solution. Hence, asking it again if anyone can help !!
I am trying to change a my working directory inside a shell script with help of a variable. But I get " No such file or directory" everytime.
#!/bin/bash
echo ${RED_INSTANCE_NAME} <-- This correctly displays the directory name
cd $RED_INSTANCE_NAME <-- This line gives the error
Now, when I try to give the actually directory name instead of using the variable, shell changes the directory without issues
cd test <-- No error
Does anyone knows what can be the issue here ? Please help !!

You variable contains a carriage return. Try saying:
cd $(echo $RED_INSTANCE_NAME | tr -d '\r')
and it should work. In order to remove the CR from the variable you can say:
RED_INSTANCE_NAME=$(echo $RED_INSTANCE_NAME | tr -d '\r')
The following would illustrate the issue:
$ mkdir abc
$ foo=abc$'\r'
$ echo "${foo}"
abc
$ cd "${foo}"
: No such file or directory
$ echo $foo | od -x
0000000 6261 0d63 000a
0000005
$ echo $foo | tr -d '\r' | od -x
0000000 6261 0a63
0000004
$ echo $'\r' | od -x
0000000 0a0d
0000002

One way to encounter your described problem is to have a tilde (~) in the variable name. Use the absolute path or $HOME variable instead. Note that using $HOME will require double quotations.
# doesn't work
$ vartilde='~/'
$ cd $vartilde
-bash: cd: ~: No such file or directory
# works
$ varfullpath='/Users/recurvirostridae'
$ cd $varfullpath
# works
$ varwithhome="$HOME"
$ cd $varwithhome

Try
cd "$RED_INSTANCE_NAME"
Also, make sure the path makes sense to the current directory where cd command is executed.

I ran into a different issue. My "cd $newDir" was failing because I added logging into my script. Apparently if you add a pipe to any cd command it does nothing or gets gobbled up.
Wasted 3 hours figuring that out.
newDir=$oldDir/more/dirs/
cd $newDir #works
cd $newDir | tee -a log #does nothing
cd $newdir | echo hi #does nothing
So cd with any pipe does nothing. No idea why cd fails. The pipe means finish what command you doing then feed any output to next command. This is on RHEL 7.
I was trying to log all my commands and hit this nice error. Figured I'd post it in case anyone else hits it.

You can check for carriage returns, ANSI escapes and other special characters with
cat -v <<< "$RED_INSTANCE_NAME"
This will show all the characters that echo $RED_INSTANCE_NAME would just hide or ignore.
In particular, if your error message is : No such file or directory as opposed to bash: cd: yourdir: No such file or directory, it means you have a carriage return at the end of your variable, probably from reading it from a DOS formatted file.

I don't know what is going wrong for you, but I can offer one piece of general advice:
cd "$RED_INSTANCE_NAME" # Quote the string in case it has spaces.error
You should nearly always put the "$VARIABLE" in quotes. This will protect from surprises when the value of the variable contains funny stuff (like spaces).

Related

Bash Script - No file exist in ~/.ssh/

I'm trying to copy a file from: ~/.ssh/
but everytime I run the script it keeps saying
pi#raspberrypi:/etc/greenwich $ ./copybash.sh
cat: ~/.ssh/testfilegen2.log: No such file or directory
copybash.sh
!/bin/bash
sourceFile="~/.ssh/testfilegen2.log"
targetFile="/etc/network/interfaces2"
sudo cat "$sourceFile" > "$targetFile"
sudo service networking restart
Any Suggestions?
Thank you
Unquote the tilde in the assignment to sourceFile so that it expands properly. Tilde expansion does not occur on parameter expansion.
sourceFile=~/".ssh/testfilegen2.log"
(In this case, no quotes would be necessary at all, but just to demonstrate that the ~ and the following / are the only things that need to remain unquoted for tilde expansion to occur.)
Take a look to this snippet code:
#!/bin/bash
v1=~/'file1.txt'
v2=~/'file2.txt'
echo 'Hi!' > $v1
cat $v1 > $v2
cat $v2
$ script.sh
Hi!
The documentation is in the section "Tilde Expansion"
of the "General Commands Manual BASH".

Why can't I run my shell script to list users?

users='awk '{print $1}' /etc/passwd | sort -u'
for user in $users
do
echo " - $user"
done
this is my shell script . Problem is that show's an error.
the error is ---> users: command not found
please give me the solution frinds
With the code the way it is now I see that you're not assigning the output of the awk|sort command to the variable (maybe you wanted to use ` instead of ' ?)
This works:
#!/bin/bash
users=$(awk '{print $1}' /etc/passwd | sort -u)
for user in $users
do
echo " - $user"
done
Although you should be aware that /etc/passwd is not separated by spaces, so awk '{print $1}' won't give you the user's name (which maybe is what you wanted)
Edit:
As per #Andy Lester's comment to your question: If you save this code in a file (let's say /tmp/myscript.bash) to run it you have to type in a terminal:
/bin/bash /tmp/myscript.bash
or, since it starts with #!/bin/bash (read here) you could make it executable (using chmod u+x /tmp/myscript.bash) and then call it, just typing /tmp/myscript.bash. You can also save it in one of the PATH directories (type echo $PATH to see which are they), make it executable and then you'll be able to call it from anywhere, but I don't really recommend doing that because you may end up overwriting juicy system's commands if you're not careful. For instance, let's say you call your script with the unfortunate name of ls, save it in the first directory of the $PATH (in my case, /usr/local/sbin) Every time you type ls, you won't be listing directories, but calling your script... Which is bad.

use of sed substitute in shell script doesn't work

I have made an install script which basically inserts some RewriteRule right after RewriteEngine On line by using sed inside a shell script
#!/bin/bash
REWRITE_RULE="RewriteRule \^catalog\/category\/view\/id\/([0-9]+)$ http:\/\/localhost\/app\/#?c=$1 [NE,L,R=301]"
FILE_PATH=".htaccess"
sed -i "s/RewriteEngine on/RewriteEngine on\n\n$REWRITE_RULE/g" $FILE_PATH
it does nothing but gives some error like
: No such file or directory
I tried same commands in shell and it worked without any issues and updated my .htaccess file
I am new to shell scripting so don't know the difference of using same command through shell and through script.. please guide
The script itself is fine. Are you sure that the lines posted above are exactly as you took them from your script?
The error message indicates that you provided an empty name as the filename, thus I suggest you put a
echo "FILE_PATH: $FILE_PATH"
directly before your sed command in order to check whether the variable is set correctly.
You'll find that your script contains carriage returns due to being saved with DOS end-of-line characters. Use tr -d '\r' <brokenscript >fixedscript to fix your script.
Here's a copy of a session with the problem reproduced and fixed:
$ cat file
var=foo
$ cat myscript
sed -i s/foo/bar/ file
$ bash myscript
: No such file or directory
$ shellcheck myscript
In myscript line 1:
sed -i s/foo/bar/ file
^-- SC1017: Literal carriage return. Run script through tr -d '\r' .
$ tr -d '\r' < myscript > fixedscript
$ bash fixedscript
$ cat file
var=bar
$

How to execute Linux shell variables within double quotes?

I have the following hacking-challenge, where we don't know, if there is a valid solution.
We have the following server script:
read s # read user input into var s
echo "$s"
# tests if it starts with 'a-f'
echo "$s" > "/home/user/${s}.txt"
We only control the input "$s". Is there a possibility to send OS-commands like uname or do you think "no way"?
I don't see any avenue for executing arbitrary commands. The script quotes $s every time it is referenced, so that limits what you can do.
The only serious attack vector I see is that the echo statement writes to a file name based on $s. Since you control $s, you can cause the script to write to some unexpected locations.
$s could contain a string like bob/important.txt. This script would then overwrite /home/user/bob/important.txt if executed with sufficient permissions. Sorry, Bob!
Or, worse, $s could be bob/../../../etc/passwd. The script would try to write to /home/user/bob/../../../etc/passwd. If the script is running as root... uh oh!
It's important to note that the script can only write to these places if it has the right permissions.
You could embed unusual characters in $s that would cause irregular file names to be created. Un-careful scripts could be taken advantage of. For example, if $s were foo -rf . bar, then the file /home/user/foo -rf . bar.txt would be created.
If someone ran for file in /home/user; rm $file; done they'd have a surprise on their hands. They would end up running rm /home/user/foo -rf . bar.txt, which is a disaster. If you take out /home/user/foo and bar.txt you're left with rm -rf . — everything in the current directory is deleted. Oops!
(They should have quoted "$file"!)
And there are two other minor things which, while I don't know how to take advantage of them maliciously, do cause the script to behave slightly differently than intended.
read allows backslashes to escape characters like space and newline. You can enter \space to embed spaces and \enter to have read parse multiple lines of input.
echo accepts a couple of flags. If $s is -n or -e then it won't actually echo $s; rather, it will interpret $s as a command-line flag.
Use read -r s or any \ will be lost/missinterpreted by your command.
read -r s?"Your input: "
if [ -n "${s}" ]
then
# "filter" file name from command
echo "${s##*/}" | sed 's|^ *\([[:alnum:]_]\{1,\}\)[[:blank:]].*|/home/user/\1.txt|' | read Output
(
# put any limitation on user here
ulimit -t 5 1>/dev/null 2>&1
`${read}`
) > ${OutPut}
else
echo "Bad command" > /home/user/Error.txt
fi
Sure:
read s
$s > /home/user/"$s".txt
If I enter uname, this prints Linux. But beware: this is a security nightmare. What if someone enters rm -rf $HOME? You'd also have issues with commands containing a slash.

Can Bash History Expansion be used to reference something in the same command?

Bash has this feature called history expansion where you can use shortcuts that expand to things you've typed previously into bash.
Example: !! - expands into previous command
$> rm -f /var/log/access.log
rm: /var/log/access.log: Permission Denied
$> sudo !!
sudo rm -f /var/log/access.log
$> echo "i am teh hax"
Another: !$ - expands into last arg of previous command
$> echo "no one was here" > access.log
$> cp !$ /var/log/
cp access.log /var/log/
Does bash, or some other shell, have the ability to use substitution shortcuts within the command itself?
Something like
$> cp httpd.conf !$.bak
cp httpd.conf httpd.conf.bak
$> echo "SABOTEUR!!!" > httpd.conf
I need to up my 1980's cyberpunk skills. Please Help.
by within the command itself, do you mean you want to refer to httpd.conf?
Then this is your solution in superuser
Using bash history expansion:
mv path/to/oldfile !#:1:h/newfile
where !#:1:h means: from the line you're currently typing (!#), take the first word (:1), then take only the path component (:h -- the head) from it.
The answer by justhalf is what you want.
But for your requirement, there is one more hack/misuse available.
sed -i.bak '' /path/to/file
e.g.
sed -i.bak '' httpd.conf
It will copy your file to another file with .bak appended.
Advantage: /path/to/file can contain wildcards/globs, or you can directly give a white-space separated list of files.

Resources