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

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".

Related

How to remove a file called * (asterisk) without using quotations?

I implemented the following command to create a file called * (asterisk):
echo > '*'
Now I'm supposed to remove this file without using any quotations.
I know how to remove this by using quotations, but not sure how without using quotations.
I tried the following commands which I was sure that they won't work because of command line expansion:
rm ./*
rm /*
If someone can help me with this, I would greatly appreciate it.
I think you're supposed to work this out yourself :-)
The simplest solution not involving quoting is to use the pattern [*]. Bracket expressions in a shell work much like character classes in regular expressions so that will match a file whose name is the single character *. Thus, you can delete your file with
rm [*]
Note that you cannot use that pattern to create a file named * because the shell substitutes words containing patterns with the name(s) of the files which match the pattern; if no such file exists, then the pattern is not matched and no substitution is performed. So if there is no file named *, then touch [*] will create a file named [*].
You could use history expansion. If the rm command directly follows the echo command, you can use !$:
echo > '*'
rm !$
!$ is shorthand for !!:$: repeat the last word ($) of the last command (!!).
If there are commands between the echo and the rm command, you can find the history number using fc -l:
$ echo > '*'
$ cmd1
$ cmd2
$ cmd3
$ fc -l
[...]
27628 echo > '*'
27629 cmd1
27630 cmd2
27631 cmd3
$ rm !27628:$
!27628 expands to the command with that number in the history, and $ is again the last word of that command.
If you have to run this in a script, you can't really look up the command number and insert it, but you can count the number of commands between the echo and the rm and use a relative event designator:
echo > '*'
cmd1
rm !-2:$
where !-2 refers to the command two lines back. Notice that history expansion is by default disabled in non-interactive shells; use
shopt -o history
to enable it.
You could use rm -i * if the number of files is not too big. This will ask for confirmation for every single file. Confirm deletion only for the file * and reject it for all others.

Why the Linux command CP behave differently in CLI and in script?

I want to copy a bunch of Verilog/systemverilog sources, so I use CP with wildcard expression:
cp <some_dir>/*.{v,sv,svh} .
It works. But when I put it to a script with exactly the same line, the CP command fails with the log:
cp: cannot stat `../../mytest/spiTest/*.{v,sv,svh}': No such file or directory
How is that happening?
PS: I use bash as the shell.
And here is my script:
#!/bin/bash
rdir=../../mytest/spiTest
f1="$rdir/bench.lst"
f2="$rdir/cphex" #the script to copy rom data
f3="$rdir/make*" #makefile scripts
f4="$rdir/*.hex" #rom files
f5="$rdir/*.{v,sv,svh}" #testbench files
echo 'Copying files...'
cp $f1 $f2 $f3 $f4 .
cp $f5 .
I do changed the first line to
#!/bin/bash -vx
and run this script again, and I get:
#!/bin/bash -vx
rdir=../../mytest/spiTest
+ rdir=../../mytest/spiTest
f1="$rdir/bench.lst"
+ f1=../../mytest/spiTest/bench.lst
f2="$rdir/cphex" #the script to copy rom data
+ f2=../../mytest/spiTest/cphex
f3="$rdir/make*" #makefile scripts
+ f3='../../mytest/spiTest/make*'
f4="$rdir/*.hex" #rom files
+ f4='../../mytest/spiTest/*.hex'
f5="$rdir/*.{v,sv,svh}" #testbench files
+ f5='../../mytest/spiTest/*.{v,sv,svh}'
echo 'Copying files...'
+ echo 'Copying files...'
Copying files...
cp $f1 $f2 $f3 $f4 .
+ cp ../../mytest/spiTest/bench.lst ../../mytest/spiTest/cphex ../../mytest/spiTest/makefile ../../mytest/spiTest/makefile.defines ../../mytest/spiTest/rom.hex ../../mytest/spiTest/rom_if.hex .
cp $f5 .
+ cp '../../mytest/spiTest/*.{v,sv,svh}' .
cp: cannot stat `../../mytest/spiTest/*.{v,sv,svh}': No such file or directory
Check the first line of the script. It probably reads:
#!/bin/sh
which switches the shell from BASH to Bourne Shell. Use
#!/bin/bash
instead.
[EDIT] You're running into problems with expansion. BASH has a certain order in which it expands patterns and variables. That means:
f5="$rdir/*.{v,sv,svh}" #testbench files
is quoted, so no file name expansion happens at this time. Only the variable $rdir is expanded. When
cp $f5 .
is executed, BASH first looks for file names to expand and there are none. Then it expands variables (f5) and then calls cp with two arguments: ../../mytest/spiTest/*.{v,sv,svh} and .. Since cp expects the shell to have performed the file name expansion already, you get an error.
To fix this, you have to use arrays:
f5=($rdir/*.{v,sv,svh})
This replaces the variable and then expands the file names and puts everything into the array f5. You can then call cp with this array while preserving whitespaces:
cp "${f5[#]}" .
Every single character here is important. [#] tells BASH to expand the whole array here. The quotes say: Preserve whitespace. {} is necessary to tell BASH that [#] is part of the variable "name" to expand.
Here's the problem: the order of substitutions. Bash performs brace expansion before variable expansion. In the line cp $f5 ., bash will do:
brace expansion: n/a
this is the key point: the variable contains a brace expression, but the shell does not see it now when it needs to.
tilde expansion: n/a
parameter expansion: yes -- cp ../../mytest/spiTest/*.{v,sv,svh} .
command substitution: n/a
arithmetic expansion: n/a
process substitution: n/a
word splitting: n/a
filename expansion: yes, bash looks for files in that directory ending with the string
.{v,sv,svh}. It finds none, nullglob is not set, thus the pattern is not removed from the command
quote removal: n/a
Now the command is executed and fails with the error you see.
https://www.gnu.org/software/bash/manual/bashref.html#Shell-Expansions
Solutions:
use Aaron's idea of an array
(not recommended) force a 2nd round of expansions: eval cp $f5 .
The line
f5="$rdir/*.{v,sv,svh}" #testbench files
is probably wrong. First, avoid comments at end of line, they should be (at least for readability) in a separate line. Then, avoid using globbing in variable assignment. So remove that line, and code later (that is, replace the old cp $f5 . line with)
cp "$rdir"/*.{v,sv,svh} .
BTW, I would test that "$rdir" is indeed a directory with
if [ ! -d "$rdir" ] ; then
echo invalid directory $rdir > /dev/stderr
exit 1
fi
You should read the Advanced Bash Scripting Guide

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
$

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.

Shell script change directory with variable

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).

Resources