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.
Related
I have a file structure like
folder/subfolder1/folders.txt,docker-compose.yml
folder/subfolder2/folders.txt,docker-compose.yml
These text files contain folders to be created in my $HOME, like appdata/subfolder1,subfolder2
I’m trying to create a script that does the following;
It should go over all the files in folder/* and watch for .txt files in subfolders. If there is no text file in subfolder1 I want it to echo “no txt file in subfolder1” but if there is a txt file in subfolder1 it should read it line by line and create the folders in my $HOME. It should also do the same for subfolder2 and so on.
It should do nothing with the .yml files in the directories.
I think it can be accomplished with something like
#!/bin/env bash
folder=“folder/*/*”
for f in $folder do
if [ $f = *.txt ] then xargs mkdir -p
else echo “no txt files in folder..”
fi
done
I think there could be a $(dirname $f) involved for echoing “no txt files in subfolder..”
I know I’m probably making a lot of mistakes but that’s why I came here, maybe some of you would like to help.
The reason for this script is I want to automate docker stack deploy for my docker swarm but unlike a regular docker-compose up swarm doesn’t create folders if they don’t exist.
Please just ask if I need to explain more about what it should and shouldn’t do.
EDIT:
I came up with some scripts but now I’m trying to combine the two,
cokehotdog#testvm:~$ for file in $files; do [[ $file == *.txt ]] && echo "$(basename $file) in $(basename $(dirname $file)) is a txt file" || echo "$(basename $file) in $(basename $(dirname $file)) is not a txt file"; done
test.txt in app1 is a txt file
test1.yml in app1 is not a txt file
test2.txt in app2 is a txt file
test2.yml in app2 is not a txt file
This should be combined with this
cokehotdog#testvm:~$ for file in $files; do [[ $file == *.txt ]] && cat $file | xargs mkdir -p -- && while read -r line; do echo "$line created";done < $file ; done
/home/cokehotdog/appdata/app1 created
/home/cokehotdog/appdata/app2 created
But when I try something like this I get an error and I’m stuck now
cokehotdog#testvm:~$ for file in $files; do [[ $file == *.txt ]] && cat $file | xargs mkdir -p -- && while read -r line; do echo "$line created";done < $file || echo "$(basename $file) in $(basename $(dirname $file)) is not a txt file"; done
cat: 'test/*/*.txt': No such file or directory
mkdir: missing operand
Try 'mkdir --help' for more information.
Try:
find folder -type f -name 'folders.txt' |
while IFS= read -r l; do
(
cd "$(dirname "$l")"
xargs -d '\n' -t echo mkdir -p < "$l"
)
done
I am trying to move files in folders according to a number in their names.
Files are names like fooNNN_bar.txt I would like to organise them like /NNN/fooNNN_bar.txt
Here is what I have for now. It prints me the folder each file would have to move to. I'm not sure how to collect the number to add it into a mv command. Is this even the correct way to do it?
#!/bin/bash
for filename in foo*.txt;
do
echo "${filename}" | grep -Eo '[0-9]{1,4}';
done
Assuming your grep works as you want:
#!/bin/bash
for filename in foo*.txt; do
num=$(echo "${filename}" | grep -Eo '[0-9]{1,4}')
mkdir -p "$num"
mv "$filename" "$num"
done
I have 224 pdf files and I'd like to prefix the files with a number and _
Example:
stackoverflow_nov_2014.pdf
File accounts.csv contains:
2567,stackoverflow
So the goal is to take 2567 and prefix it to the pdf file with an underscore:
2567_stackoverflow_nov_2014.pdf
I think I would want to use read -r in a while loop as explained here:
https://unix.stackexchange.com/questions/147569/rename-a-batch-of-files-after-reading-from-a-source-file
But when attempting this as it's written, the shell gives usage of mv command and nothing changes with the files.
Edit: Adding sample data from sources file (accounts.csv)
11,My_Golf_Shop
2567,stackoverflow
11122,Test_Store
By the way, the sources file (accounts.csv) isn't in the same order as the files in the directory as accounts.csv so somehow there would need to be matching with file name and the accounts.csv that occurs.
Below is the script that should work under the assumption:
1. All the files are under the same folder
2. For a particular prefix, this script will only rename the first found file.
#!/bin/bash
while read -r line; do
num=`echo $line |cut -f 1 -d ","`
prefix=`echo $line |cut -f 2 -d ","`
if [ -n "$num" -a -n "$prefix" ]; then
full_file=$(basename `eval find . -name '$prefix\*' -print -quit` 2>/dev/null )
mv $full_file ${num}_$full_file 2>/dev/null
fi
done < accounts.csv
I have a tarball Index.tar.gz. Inside that I have directories like this
Index1/db_newtime_oldtime_0
Index1/db_newtime_oldtime_1
Index1/db_newtime_oldtime_2
Index2/db_newtime_oldtime_0
Index2/db_newtime_oldtime_1
Index2/db_newtime_oldtime_2
While extracting the tar file, I want to add 99 with the numeric value at the end of the db directory name.
So after extraction the directory structure should be like this
Index1/db_newtime_oldtime_99
Index1/db_newtime_oldtime_100
Index1/db_newtime_oldtime_101
Index2/db_newtime_oldtime_99
Index2/db_newtime_oldtime_100
Index2/db_newtime_oldtime_101
So is it possible to rename like this by using shell script ?
after extracting tar ball you can just rename each of the directory names inside index*/...
You can use a for loop to do that :
for i in $(\ls -d index*/*);
do
dst=$(echo "$i" | sed -e 's/oldtime_/oldtime_ /g' | awk '{print $1$2+99}');
mv "$i" "$dst";
done
Going to test this. If not missed anything while tar-gzipping....
tar -C ./tmp -xvf ./Index.tar.gz
find ./tmp -name "[a-zA-Z_][0-9]" | sed s/([a-zA-Z])([0-9])/\1 \2 | awk '{print "mv "$1$2" "$1$2+99}'
cd ./tmp && tar -zxf ../Index.tar.gz . && cd ../ && rm -rf ./tmp
NOTE:
yes, AWK supports math :)
Using Bash:
tar -vxf Index.tar.gz | (
R='^(.*[^0-9])([0-9]+)$'
read A
while [[ -n $A ]]; do
read B
[[ $A =~ $R ]] && echo mv "$A" "${BASH_REMATCH[1]}$(( BASH_REMATCH[2] + 99 ))"
A=$B
done
)
Note: The -v is important. Remove echo near mv if it seems to work well already.
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