Move Files Specific Folder - bash

I need bash script, these are my files:
./2019-01-11_15-00-29_UTC.mp4
./2019-02-10_17-42-18_UTC.mp4
./2019-01-03_14-45-43_UTC.mp4
./2018-12-24_13-00-32_UTC.mp4
./2018-12-09_19-50-59_UTC.mp4
./2019-01-11_14-51-08_UTC.mp4
./2019-01-06_16-41-54_UTC.mp4
./2019-02-03_10-33-33_UTC.mp4
./2019-02-16_18-21-30_UTC.mp4
wanna make two folder 2018 & 2019 then move files own folder.
I use this code:
ls *.mp4 | awk -F"-" '{print $1}' | while read day file ; do mkdir -p "$day"; mv "$file" "$day"; done
It makes folder but not move

Aren't you getting errors? When I run it I get
mv: cannot stat '': No such file or directory
once for each file. The reason is that file isn't being set in your loop.
ls *.mp4 | awk -F"-" '{print $1}'
Will generate a list of years
2018
2018
2019
2019
2019
2019
2019
2019
2019
That's a single column of data.
while read day file
reads the year into day (day?) and since there's no more data, leaves file empty.
mkdir -p "$day"
works fine, but
mv "$file" "$day"
evaluates to
mv "" "2018"
Try this.
for f in *.mp4
do mkdir -p "${f%%-*}" && mv "$f" "${f%%-*}"
done
The ${f%%-*} just returns $f with everything from the first dash removed. The result:
$: find
.
./2018
./2018/2018-12-09_19-50-59_UTC.mp4
./2018/2018-12-24_13-00-32_UTC.mp4
./2019
./2019/2019-01-03_14-45-43_UTC.mp4
./2019/2019-01-06_16-41-54_UTC.mp4
./2019/2019-01-11_14-51-08_UTC.mp4
./2019/2019-01-11_15-00-29_UTC.mp4
./2019/2019-02-03_10-33-33_UTC.mp4
./2019/2019-02-10_17-42-18_UTC.mp4
./2019/2019-02-16_18-21-30_UTC.mp4

You can use xargs to achieve this.
ls *.mp4 | xargs -I{} sh -c 'folder=`echo {} | cut -d"-" -f1`;mkdir -p $folder;mv {} $folder/'
Here, the entire file names are sent to xargs and, for each file the folder name is obtained using the cut command. Then the file is moved into the created folder.
More about xargs: http://man7.org/linux/man-pages/man1/xargs.1.html
Edited
for file in *.mp4 ; do
date=$(echo $file | cut -d'_' -f1)
year=$(echo $date | cut -d'-' -f1)
month=$(echo $date | cut -d'-' -f2)
day=$(echo $date | cut -d'-' -f3)
mkdir -p $year/$month/$day
mv $file $year/$month/$day/
done

Related

Ubuntu script to rename every file in folders and sub directories NOT matching sha1sum

On Ubuntu i have run this command.
sha1sum /home/abcd/random/1/1.mp4
So I have the following directory
/home/abcd/
Inside this folder are sub folders
/home/abcd/1
/home/abcd/2
etc
etc
These sub folders are filled with video files .mp4 extensions
They have multiple files with a file hash of sha1sum of 3c72363260a32992c3ab2e3a5e9b8cf082e02eac i wanted to rename all the files NOT matching this file to
vid_1.mp4
vid_2.mp4
etc
etc
How can I achieve this?
pseudocode
find mp4s
sha1sum them (outputs <sha1sum> <filename>)
pass lines not 3c72363260a32992c3ab2e3a5e9b8cf082e02eac
change lines from <sha1sum> <filename> to mv "<filename>" "vid_<filename"
execute the lines
code
cd /home/abcd
find . -name "*.mp4" -print0 |
xargs -r -0 sha1sum |
awk '$1!="3c72363260a32992c3ab2e3a5e9b8cf082e02eac"' |
sed 's/^[^\s]\+\s\+\(.*\)/mv "\1" "vid_\1"/' |
sh
You can try this for loop with a nested if loop
for f in /home/abcd/*; do
i=$((i+1));
sum=$(sha1sum "$f" | awk '{print $1}');
if [[ "$sum" != 3c72363260a32992c3ab2e3a5e9b8cf082e02eac ]]; then
mv "$f" "vid_$i.mp4";
fi;
done

Create symlinks sorted by year folders

I have a holiday folder with all pictures unsorted in the same folder, all files are tagged with the year. I want to create a folder for the year. This I've solved with:
#!/bin/bash
SOURCE=/path/to/pictures
cd ~/pictures/sorted.by.year/
ls $SOURCE | grep -o -P '(?<!\d)\d{4}(?!\d)' | tr '/' '\n' | sort |
uniq | xargs mkdir
Now to my problem, I want to create a symlink to all year folders from the filename so it will create symlinks, like this:
~/pictures/sorted.by.year/2014/symlink-picture.on.sister.2014.jpg
~/pictures/sorted.by.year/2012/symlink-picture.on.dad.2012.jpg
I have tried with xargs but the result was not as I wanted in the example above.
Does someone have an idea how to do it?
Please check this script, I added some more line after your script:
#!/bin/bash
SOURCE=/path/to/pictures
cd ~/pictures/sorted.by.year/
ls $SOURCE | grep -o -P '(?<!\d)\d{4}(?!\d)' | tr '/' '\n' | sort | uniq | xargs mkdir
for file in /path/to/pictures/*
do
if [[ -d $file ]]; then # Skip directory
continue;
fi
DIRNAME=$(echo "$file" | grep -o -P '(?<!\d)\d{4}(?!\d)');
ln -s "$file" "./$DIRNAME/"
done

Move files into separate subfolders

I've been trying to write a shell script for a while and am just stumped. I'm on a mac and have some knowledge of using UNIX but I'm stumped. I have a list of files:
folder1_123
folder1_abc
folder2_654
folder2_zxy
and I want them to be like this:
folder1/123
folder1/abc
folder2/654
folder2/zxy
so far I've gotten a script that looks like this:
for file in *_.*; do
dir=${file%%.*}
mkdir -p "$dir"
mv "$file" "$dir"
done
Check below one liner script for the same. Run this script in the directory containing all files in the format _. It will iterate over each file name will take first part before _ and will create a directory with that name.After that it will move the file from parent directory to newly created directory,until its done with all files.
for i in `ls |grep _`;do mkdir -p `echo $i|cut -f1 -d "_"`;mv $i `echo $i|cut -f1 -d "_"`/`echo $i|cut -f2 -d "_"`;done
You can split file/dir name with 'cut':
for file in *_*; do
DIRNAME=$(echo $file | cut -d"_" -f1)
if [[ ! -d ${DIRNAME} ]]; then
mkdir ${DIRNAME}
fi
FILENAME=$(echo $file | cut -d"_" -f2)
mv $file ${DIRNAME}/${FILENAME}
done
The body of your loop is wrong. It should be:
dir=${file%_*}
newfn=${file#*_}
mkdir -p $dir && mv $file $dir/$newfn
Depending on your requirements, you might or might not want to add a guard
if [[ -f $file ]]
then
...
fi
around this.

Reporting with cut and grep

I'm trying to create a script that gets an extension and reports in two columns, the user and the amount of files that user owns with that extension.
The results must be printed in report.txt
Here is my code.
#!/bin/bash
#Uncoment to create /tmp/test/ with 50 txt files
#mkdir /tmp/test/
#touch /tmp/test/arch{01..50}.txt
clear
usage(){
echo "The script needs an extension to search"
echo "$0 <extension>"
}
if [ $# -eq 0 ]; then
usage
exit 1
fi
folder="/tmp/test/"
touch report.txt
count=0
pushd $folder
for file in $(ls -l); do
grep "*.$1" | cut -d " " -f3 >> report.txt
done
popd
The program just runs endlessly. And I'm not even counting the files for each user.
How can I solve this using only grep and cut?
With GNU stat :
stat -c '%U' *."$1" | sort | uniq -c | awk '{print $2,"\t",$1}' > report.txt
As pointed out by mklement0, under BSD/OSX you must use a -f option with stat :
stat -f '%Su' *."$1" | sort | uniq -c | awk '{print $2,"\t",$1}' > report.txt
Edit :
To process many files and avoid argument number limitation, you'd better use a printf piped to the stat command (thanks again mklement0) :
printf '%s\0' *."$1" | xargs -0 stat -c '%U' | sort | uniq -c | awk '{print $2,"\t",$1}'
You don't need a loop for this (unless you later need to loop over several folders), and changing the working directory in a script is rarely necessary. Also, reading ls output is usually not recommended.
Here's a version that replaces the loop, and uses du:
ext="$1"
printf "Folder '%s':\t" "$folder" >>report.txt
du -hc "$folder"/*."$ext" | sed -n '$p' >>report.txt

Bash: reading files and moving into sub-directory

I have around 1000 files (png) and need to move them into the corresponding directory and their sub-directory.
I do have 26 directories (A - Z) and below each directory the complete alphabet A-Z again. File names are 6 characters/digits long and have a png extension, e.g. e.g. AH2BC0.png
I would need to move the file AH2BC0.png into the directory A and within that directory into the sub-directory H, e.g.A->H->AH2BC0.png.
I have created following script which is not really working as expected:
#!/bin/bash
ls >LISTE.txt
for i in LISTE.txt; do
a=$(cat $i | cut -b 1 | tr '[:lower:]' '[:upper:]')
b=$(cat $i | cut -b 2 | tr '[:lower:]' '[:upper:]')
mkdir -p $a/$b
cat $i | xargs mv $a/$b
rm $i
done
Problem is that a) the sub-directory is not created and b) the files are not moved. Any suggestions or better ideas for the script?
Thanks
PS: I guess it's obvious that it's quite some years ago that I have created any bash scripts or coded so please bear with me.
PSS: working on MAC OSX bash 3.2
There's already a post showing a better program to do what you want but I thought I'd show you how to fix yours. Hopefully you'll find it informative.
#!/bin/bash
ls >LISTE.txt
for i in LISTE.txt; do
This loops over the single value LISTE.txt; replace it with:
for i in $(cat LISTE.txt); do
to loop over the contents of the file instead.
a=$(cat $i | cut -b 1 | tr '[:lower:]' '[:upper:]')
b=$(cat $i | cut -b 2 | tr '[:lower:]' '[:upper:]')
You want to use echo rather than cat in the above two lines, as you're after the name of the file not its content.
mkdir -p $a/$b
cat $i | xargs mv $a/$b
I don't think the above line does what you think it does... It will attempt to rename the $a/$b directory to C, where C is the content of file $i. Replace it with:
mv $i $a/$b
The following line is not needed:
rm $i
So simply delete it. It would only be necessary if you copied rather than moved the files using mv.
done
Here's your complete program after the changes I've suggested.
#!/bin/bash
ls >LISTE.txt
for i in $(cat LISTE.txt); do
a=$(echo $i | cut -b 1 | tr '[:lower:]' '[:upper:]')
b=$(echo $i | cut -b 2 | tr '[:lower:]' '[:upper:]')
mkdir -p $a/$b
mv $i $a/$b
done
#!/bin/bash
for item in *; do
first=${item:0:1}
second=${item:1:1}
folder="$first/$second"
mkdir -p $folder
mv $item $folder/
done

Resources