rename file instead of directory in filepath - file-rename

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

Related

How do I extract a file name into 2 parts, making one into directory and the other one inside of it?

I'm trying to sort all mp3 files by artist and name. At the moment, they're in 1 giant file name.
E.g Artist - Song name.mp3
I want to convert this to
Artist/Song name.mp3
Here's what I've tried so far. In this case, I was using a test file named "hi\ -\ hey":
#!/bin/bash
# filters by all .mp3 extensions in the current working directory
for f in *.mp3; do
# extract artist and song name and remove spaces
artist=${f% -*}
song=${f#*- }
#make directory with the extracted artist name and move + rename the file into the directory
mkdir -p $artist
mv $f $artist/$song;
done
For some reason, it's creating a directory with the song name instead of the artist in addition to a load of errors:
mv: cannot move 'hey.mp3' to a subdirectory of itself, 'hey.mp3/hey.mp3'
mv: cannot stat 'hi': No such file or directory
mv: cannot stat '-': No such file or directory
mv: 'hey.mp3/hi' and 'hey.mp3/hi' are the same file
mv: cannot stat '-': No such file or directory
By far the simplest way of doing this is to use rename a.k.a. Perl rename.
Basically, you want to replace the sequence SPACE-DASH-SPACE with a forward slash directory separator, so the command is:
rename --dry-run -p 's| - |/|' *mp3
Sample Output
'Artist - Song name.mp3' would be renamed to 'Artist/Song name.mp3'
'Artist B - Song name 2.mp3' would be renamed to 'Artist B/Song name 2.mp3'
If that looks correct, just remove --dry-run and run it again for real. The benefits of using rename are:
it can do a dry-run to test before you run for real
it will create all necessary directories with the -p option
it will not clobber (overwrite) files without warning
you have the full power of Perl available to you and can make your renaming as sophisticated as you wish.
Note that you can install on macOS with homebrew:
brew install rename
Just in case you don't have the rename utility. A fix on your original script.
for f in *.mp3; do
# extract artist and song name and remove spaces
artist=${f%% -*}
song=${f##*- }
#make directory with the extracted artist name and move + rename the file into the directory
echo mkdir -p -- "$artist" && echo mv -- "$f" "$artist/$song"
done
Remove the echo If you're satisfied with the output.
Assuming there are many files, it's probably much faster to do this using pipes instead of a for loop. This has the additional advantage of avoiding complicated bash-specific syntax and using core unix/linux command line programs instead.
find *-*.mp3 |
sed 's,\([^-]\+\)\s*-\s*\(.*\),mkdir -p "\1"; mv "&" "\1"/"\2",' |
bash
Explanation:
This find to find all the files matching -.mp3 in the current directory.
This sed command changes each line to a command string, e.g.:
aaa - bbb.mp3
->
mkdir -p "aaa"; mv "aaa - bbb.mp3" "aaa"/"bbb.mp3"
The bash command runs each of those command strings.
you can try this.
#!/usr/local/bin/bash
for f in *.mp3
do
artist=`echo $f | awk '{print $1}' FS=-`
song=`echo $f | awk '{print $2}' FS=-`
mkdir -p $artist
mv $artist-$song $song
mv $song ./$artist
done
here I am using two variable artist and song. as your test file name is "hi\ -\ hey" so I change the awk delimiter to "-" to store variable according to it.
we don't need to use awk..by using bash parameter expansion.... it is working.
#!/usr/local/bin/bash
for f in *.mp3
do
artist=`echo ${f%-*}`
song=`echo ${f#*-}`
mkdir -p $artist
mv $artist-$song $song
mv $song ./$artist
done

How to move files from subfolders to their parent directory (unix, terminal)

I have a folder structure like this:
A big parent folder named Photos. This folder contains 900+ subfolders named a_000, a_001, a_002 etc.
Each of those subfolders contain more subfolders, named dir_001, dir_002 etc. And each of those subfolders contain lots of pictures (with unique names).
I want to move all these pictures contained in the subdirectories of a_xxx inside a_xxx. (where xxx could be 001, 002 etc)
After looking in similar questions around, this is the closest solution I came up with:
for file in *; do
if [ -d $file ]; then
cd $file; mv * ./; cd ..;
fi
done
Another solution I got is doing a bash script:
#!/bin/bash
dir1="/path/to/photos/"
subs= `ls $dir1`
for i in $subs; do
mv $dir1/$i/*/* $dir1/$i/
done
Still, I'm missing something, can you help?
(Then it would be nice to discard the empty dir_yyy, but not much of a problem at the moment)
You could try the following bash script :
#!/bin/bash
#needed in case we have empty folders
shopt -s nullglob
#we must write the full path here (no ~ character)
target="/path/to/photos"
#we use a glob to list the folders. parsing the output of ls is baaaaaaaddd !!!!
#for every folder in our photo folder ...
for dir in "$target"/*/
do
#we list the subdirectories ...
for sub in "$dir"/*/
do
#and we move the content of the subdirectories to the parent
mv "$sub"/* "$dir"
#if you want to remove subdirectories once the copy is done, uncoment the next line
#rm -r "$sub"
done
done
Here is why you don't parse ls in bash
Make sure the directory where the files exist is correct (and complete) in the following script and try it:
#!/bin/bash
BigParentDir=Photos
for subdir in "$BigParentDir"/*/; do # Select the a_001, a_002 subdirs
for ssdir in "$subdir"/*/; do # Select dir_001, … sub-subdirs
for f in "$ssdir"/*; do # Select the files to move
if [[ -f $f ]]; do # if indeed are files
echo \
mv "$ssdir"/* "$subdir"/ # Move the files.
fi
done
done
done
No file will be moved, just printed. If you are sure the script does what you want, comment the echo line and run it "for real".
You can try this
#!/bin/bash
dir1="/path/to/photos/"
subs= `ls $dir1`
cp /dev/null /tmp/newscript.sh
for i in $subs; do
find $dir1/$i -type f -exec echo mv \'\{\}\' $dir1/$i \; >> /tmp/newscript.sh
done
then open /tmp/newscript.sh with an editor or less and see if looks like what you are trying to do.
if it does then execute it with sh -x /tmp/newscript.sh

cp cannot stat to ~/ in script

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

Remove files from one folder that contained in another folder

I'am trying to write simple script that will get files name from one folder and search them in another folder and remove if found them in that folder.
Got two folder like
/home/install/lib
/home/install/bin
/home/install/include
and
/usr/local/lib
/usr/local/bin
/usr/local/include
I want to remove all file's from /usr/local/lib{bin,include} that contains in /home/install/lib{bin,include}. For example having
/home/install/lib/test1
/usr/local/lib/test1
scritp will remove /usr/local/lib/test1. I tried to do it from each separate directory
/home/install/lib:ls -f -exec rm /usr/local/lib/{} \;
but nothing. Can you help me to manage with this simple script?
Create script rmcomm
#!/bin/bash
a="/home/install/$1"
b="/usr/local/$1"
comm -12 <(ls "$a") <(ls "$b") | while read file; do
rm "$b/$file"
done
Then call this script for every pair:
for dir in lib bin include; do rmcomm "$dir"; done
Here's something simple. Remove the echo from the line containing rm to run it after you've ensured it's doing what you want:
#!/bin/bash
dirs[0]=lib
dirs[1]=bin
dirs[2]=include
pushd /home/install
for dir in "${dirs[#]}"
do
for file in $(find $dir -type f)
do
# Remove 'echo' below once you're satisfied the correct files
# are being removed
echo rm /usr/local/$file
done
done
popd

How to replace backslashes from windows formatted files into *nix format and reestablish structure

I have a fair few files that have been extracted from a zip created in a strange way.
the files have come out of the tar.gz in windows file structure format
Example:
jpg_250\MI\00\00\00\19\MI0000001900.jpg
versus
jpg_250/MI/00/00/00/19/MI0000001900.jpg
The former is seen as a single file by linux.
I've been playing around with awk and sed to delimit the filename by backslash, and create the directories in question in the correct structure, and finally rename the file to the MI**.jpg and move it into the correct newly created end directory.
Is awk and sed the way to go here? I have awk exploding the filename into the 'directories' I need but I'm having trouble getting the directories actually created. I assume I would need sed at the end to rename the file into the MI**.jpg format.
Many thanks for any help.
Something like this?
$ ls
a\b\c
a\b\d
$ for i in *; do
F=$(echo $i | sed 's,\\,/,g')
D=$(dirname $F)
echo mkdir -p ${D}
echo cp "${i}" "${F}"
done
mkdir -p a/b
cp a\b\c a/b/c
mkdir -p a/b
cp a\b\d a/b/d
You could do this with Perl like this.
# assume that the script was called like this:
# myscript file1 file2 etc...
# then all the files are in #ARGV
foreach $orig (#ARGV) {
$orig_fixed = $orig;
# convert the \ to /
$orig_fixed =~ s!\\!/!g;
#split the filename into directory and filename
($dir, $filename) = ($orig_fixed =~ m!^(.*)/([^/]*)$!);
# create the directory if it doesn't exist
if (!-e $dir) {
`mkdir -p $dir`; # -p means create the full path
}
# now move the file
`mv '$orig' $dir/$filename`;
}

Resources