cp cannot stat to ~/ in script - bash

I am having a problem I just can't seem to get over in my bash script.
Whenever I try to copy using cp to home folder in a script I get
cp: cannot stat '~/file.txt': no such file or directory
My code is as follows:
#!/bin/bash
echo "file location"
read a
user inputs ~/file.txt
b=$(basename $a)
cp "$a" . /$b
Please help, it's probably a simple solution but I just can't figure it out.

Filename expansion isn't applied to variables, which can be checked with the following minimal example:
d="~"; ls $d
ls: cannot access ~: No such file or directory
Use the full path: /home/youruser/file.txt.
Alternatively, you can force the globbing with eval (but prefer not to.. it's eval..):
d=$(eval echo "$d")
echo $d # /home/user

You could do something like
expanded_path=$(echo "$d" | "s:^~:$HOME:")
(that is, subtstitute the initial ~ for $HOME manually)
or force the user to use the full path.
eval is evil (definitely for user-supplied input, it is).
If you just want to copy in the current dir while keeping the original name, you can do:
cp "$src" .
No need to play with basename.

You can just replace the ~ with $HOME:
read a
a=${a/\~/$HOME}
Now ~/file will become /home/user/file

Related

How can I compare filename without getting permission denied message in bash?

I want to look through all files in my directory and subdirectory
then delete files with special name
Here is my code
for filename in $1*;do
if("$filename" == "hello.txt");then
echo "WOW!"
fi
done
My test directory is TEST/ and there are two files. one name "hello.txt" and "world.txt";However, when I run the code I receive
noStrange.sh: line 2: TEST/hello.txt: Permission denied
noStrange.sh: line 2: TEST/world.txt: Permission denied
I tried the command chmod u+x scriptname, it doesn't work
This is what I input
sh scriptname TEST/
Can anyone tell me what is wrong with the script?
Use basename command to get the basename of a file from file path variable.
for filename in $1*;do if [[ $(basename "$filename") == "hello.txt" ]] ; then echo "wow";fi; done
Or
Use find command. This would search through all the files exists in the current folder as well it's sub folders.
find . -name 'hello.txt'
The immediate answer is that your syntax for tests is wrong; you should have
if ["$filename" == "hello.txt"]; then
etc. However, there are a few issues with your code. Since $filename will match TEST/hello.txt instead of hello.txt, you probably won't get the behavior you want. Also, if you're looking to just delete files with certain names, you probably want a normal UNIX command like
rm TEST/hello.txt
If there are patterns you want do delete, you can use glob/wildcards, or a combination of find, xargs and rm. E.g.
find TEST -name 'hello*.txt' | xargs rm

rename file instead of directory in filepath

I want to rename I file that is a variable but I am having problems.
$ mkdir -p ~/foo/fooYes/
$ touch ~/foo/fooYes/fooYes.txt
$ OUTPUT=~foo/fooYes/fooYes.txt
$ echo $OUTPUT
~/foo/fooYes/fooYes.txt
$ rename 's/fooYes/fooNo/' "${OUTPUT}"
Can't rename '/Users/medialab/foo/fooYes/fooYes.txt' to '/Users/medialab/foo/fooNo/fooYes.txt': No such file or directory
two questions:
Why is it telling me I can't rename?
More importantly, I want to rename fooYes.txt, NOT its parent directory.
I am using the simplified example above as a fill in for a shell script I have where "${OUTPUT}" is the result of an ffmpeg command. I want to rename the basename of "${OUTPUT}" but since it shares the same name as its parent directory (minus the extension, of course) it is the parent directory and not "${OUTPUT}" that gets renamed.
#!/bin/bash
mkdir -p ~/foo/fooYes/
touch ~/foo/fooYes/fooYes.txt
OUTPUT=~/foo/fooYes/fooYes.txt
echo $OUTPUT
PARENTDIR=$(dirname $OUTPUT)
echo $PARENTDIR
cd $PARENTDIR
rename 's/fooYes/FooNo/' *.txt
cd ~
shift
Not very elegant but it does exactly what I want. If anyone has a better solution please share!
You can use this also:
mv file1 file2

Do actions in each folder from current directory via terminal

I'm trying to run a series of commands on a list of files in multiple directories located directly under the current branch.
An example hierarchy is as follows:
/tmp
|-1
| |-a.txt
| |-b.txt
| |-c.txt
|-2
| |-a.txt
| |-b.txt
| |-c.txt
From the /tmp directory I'm sitting at my prompt and I'm trying to run a command against the a.txt file by renaming it to d.txt.
How do I get it to go into each directory and rename the file? I've tried the following and it won't work:
for i in ./*; do
mv "$i" $"(echo $i | sed -e 's/a.txt/d.txt/')"
done
It just doesn't jump into each directory. I've also tried to get it to create files for me, or folders under each hierarchy from the current directory just 1 folder deep, but it won't work using this:
for x in ./; do
mkdir -p cats
done
OR
for x in ./; do
touch $x/cats.txt
done
Any ideas ?
Place the below script in your base directory
#!/bin/bash
# Move 'a.txt's to 'd.txt's recursively
mover()
{
CUR_DIR=$(dirname "$1")
mv "$1" "$CUR_DIR/d.txt"
}
export -f mover
find . -type f -name "a.txt" -exec bash -c 'mover "$0"' {} \;
and execute it.
Note:
If you wish be a bit more innovative and generalize the script, you could accept directory name to search for as a parameter to the script and pass the directory name to find
> for i in ./*; do
As per your own description, this will assign ./1 and then ./2 to i. Neither of those matches any of the actual files. You want
for i in ./*/*; do
As a further aside, the shell is perfectly capable of replacing simple strings using glob patterns. This also coincidentally fixes the problem with not quoting $i when you echo it.
mv "$i" "${i%/a.txt}/d.txt"

Get directories without specific filetype cygwin/bash

I am trying to organise my music collection's album art, for XBMC etc. and my own personal OCD based reasons. To do this, I want to loop through each folder in my music collection, (there are also files in music hence $(find . -type d). I check each folder for the existence of a jpeg. If it exists, it is copied to be called cover.jpg. If no jpeg is found, the name of the folder should be added to a list, which I'll then search google for myself. The code I'm using is below.
#!/bin/bash
IFS=$'\n'
touch missing
cd /cygdrive/d/music
for i in $(find . -type d)
do cd $i
if [ -e '*.jpg' ]
then
for j in *.jpg
do cp $j 'cover.jpg'
done
else
cd ..
$i >> missing
fi
done
The Problems
cd $i always fails, as the directory does not exist.
$i >> missing always fails, with a permission is denied error, however ls -l missing gives -rwxrwxrwx+ 1 Username None 0 Sep 13 16:45 missing
Update:
After trying several iterations to get the script to work, I had to give up and simply use this Album Art Downloader, which in hindsight was the obvious thing to do. I'm marking the answer with a revised script as accepted as it got me closer to the solution I know is out there, somewhere.
1) cd $i always fails, as the directory does not exist.
since the result of find . -type -d is relative to your where you initial working directory, you'll need to cd back to the base directory before doing the nextcd $1. A quick way to return to the previous directory is cd -.
2) $i >> missing always fails, with a permission is denied error,
Two problems here. First of all, by doing $i >> missing you're actually trying to execute $i as a command. If you want the filename appended to the file, you'll need to use echo $i >> missing.
Secondly, you created missing in your initial working directory, and so you're actually dealing with a different missing file once you've cd'ed somewhere else. To refer to the same file, use an absolute path.
Try this: (I tried to keep the script as close to what you have as possible and commented on lines that I changed. I've also quoted all paths to better handle filenames with spaces)
#!/bin/bash
IFS=$'\n'
MISSING="$(pwd)/missing" # store absolute path
> "$MISSING". # create new or empty existing
cd /cygdrive/d/music
for i in $(find . -type d)
do
cd "$i"
if [ -e *.jpg ] # wildcard not expanded if quoted
then
for j in *.jpg
do cp "$j" 'cover.jpg'
done
else
echo "$i" >> "$MISSING" # use $MISSING
fi
cd - # go back to previous directory
done
p.s. Just in case you haven't noticed, do be aware that if you have more than one *.jpg file in a directory, your script will essentially repeatedly overwrite cover.jpg with the last one ending up as the actual version. Also, you're likely to see the error "cp: 'cover.jpg' and 'cover.jpg' are the same file" if you have more than one jpg file and when cover.jpg already exists.
cd $i will fail for a file like "Artist Name - Song Name.mp3". The shell will break it into Artist Name etc. on whitespace and it will break. You could print $i before doing the cd to debug the problem.
$i >> missing will fail because you're trying to execute $i as a script and output the contents into missing. If you want to put the file name, you should use something like echo "$i" >> missing.
I recommend you use a real scripting language like Perl or Python rather than do this in shell. Files with spaces in the name are a real pain.

Shell Script to update the contents of a folder - 2

I wrote this piece of code this morning.
The idea is, a text file (new.txt) has the details about the directory structure and the files in the directory.
Read new.txt, create the same directory structure at a destination directory (here it is /tmp), copy the source files to the corresponding destination directory.
Script
clear
DEST_DIR=/tmp
for file in 'cat new.txt'
do
mkdir -p $file
touch $file
echo 'ls -ltr $file'
cp -rf $file $DEST_DIR
find . -name $file -type f
cp $file $DEST_DIR
done
Contents of new.txt
Test/test1/test1.txt
Test/test2/test2.txt
Test/test3/test3.txt
Test/test4/test4.txt
The issue is, it executes the code, creates the directory structure, but instead of creating it at the end, it creates directories named test1.txt, test2.txt, etc. I have no idea why this is happening.
Another question: For Turbo C, C++, there is an option to check the execution flow? Is there something available in Unix, Perl and shell scripting to check the execution flow?
The script creates these directories because you tell it to on the line mkdir -p $file. You have to extract the directory path from you filename. The standard command for this is dirname:
dir=`dirname "$file"`
mkdir -p -- "$dir"
To check the execution flow is to add set -x at the top of your script. This will cause all lines that are executed to be printed to stderr with "+ " in front of it.
you might want to try something like rsync

Resources