Linux shell scripts for commands to update multiple folders - linux

I have a script that I created that is just a list of commands (cp's, mkdir's, rm's, etc). What this does is basically update the contents of a bunch of folders with the contents of a source folder.
Right now I have about 15 folders to be modified with about 30 commands each. So when I need to add a folder, I need to add 30 more commands and specify that folder.
Is there a way in the script to create an array of folders to change and loop through it or something?
My script right now just contains basic commands that would normally be run in the command line, so nothing advanced.

Yes, you can do something like this:
for x in "folder1" "folder2" "folder3"; do
mkdir $x
cp foobar $x
done
Better yet, use an array to hold the folder names, e.g.
arr=("folder1" "folder2" "folder3")
for x in ${arr[*]} do
mkdir $x
cp foobar $x
done
If you have specific names following a pattern, you can probably use a loop to generate that list of names automatically.

Here is one approach:
#!/bin/bash
# This function does all you clever stuff
# $1 contains the first parameter, $2 the second and so on
function my_cmds()
{
echo $1
}
# You can loop over folders like this
for i in folder1 folder2 folder3
do
# here we call a function with the string as parameter
my_cmds $i
done
# ... or like this
folders[0]="folder0"
folders[1]="folders1"
folders[2]="folders2"
for i in "${folders[#]}"
do
my_cmds $i
done
a convenient way of initializing an entire array is the
array=( element1 element2 ... elementN )
notation.

This is similar to answers using for loops, but using a here document to store the list of folders. It's like having a data file embedded in the script.
while read -r folder <&3; do
mkdir "$folder"
# etc
done 3<<EOF
folder1
folder2
folder with space
EOF
I use file descriptor 3 for the here document in case you have commands in the body of the loop that attempt to read from standard input.

Related

for each pair of files with the same prefix, execute code

I have a large list of directories, each of which contains a varied number of "paired" files. By paired, I mean the prefix is the same for two files, and the pairs are denoted as "a" and "b". The prefix does not follow a defined pattern either. My broader intentions are to write a bash script that will list all subdirectories in a given directory, cd into each directory, find the pairs of files, and execute a function on the pairs. Here is an example directory:
Dir1
123_a.txt
234_a.txt
123_b.txt
234_b.txt
Dir2
345_a.txt
345_b.txt
Dir3
456_a.txt
567_a.txt
678_a.txt
456_b.txt
567_b.txt
678_b.txt
I can use this code to loop thought each directory:
for d in ./*/ ; do (cd "$d" && script.sh); done
In script.sh, I have been working on writing a script that will find all pairs of files (which is the problem I am struggling to figure out), and then call the function I want to apply to those files. This is the gist of what I have been trying:
for file in ./*_a.txt; do (find the paired file with *_b.txt && run_function.sh); done
Ive broken the problem into needing to get the value of "*" for the _a.txt files, and then searching the directory using this value for the matching _b.txt suffix,and making a subdirectory that I can put them into so I can then apply run_function.sh. So Dir1, would contain subdirectories 123 and 234.
Let me know if this doesn't make sense. The part of the problem I'm struggling with is matching files without a defined prefix.
Thanks for your help.
Use parameter expansion:
#!/bin/bash
file=123_a.txt
prefix=${file%_a.txt} # remove _a.txt from the right
second=${prefix}_b.txt
if [[ -f $second ]] ; then
run_function "$file" "$second"
fi

How do I change directories in a shell script while inside a loop?

I have over a thousand compressed files inside each other, named in descending order starting at 1022; and need to extract all of them. What I'm trying to do is to write a shell script that will do just that. Here's what I've got so far:
cd "PATH"
for file in PATH/*; do
filename=${PWD: -4}
extract $file
numb=$(echo $filename - 1 | bc)
echo $numb
cd "$numb"
done
The "extract" function extracts any format of compression.
The problem is the loop doesn't seem to go on inside the new directory. Can anyone help me out with this?

Move Multiple sequence file using bash

I want to move sequence 20 file in different folder
so i use below code to move file but not work...
echo "Moving {$(( $j*20 -19))..$(( $j*20 ))}.png ------- > Folder $i"
mv {$(( $j*20 -19))..$(( $j*20 ))}.png $i;
So i get output in terminal
Moving {1..20}.png ------- > Folder 1
mv: cannot stat ‘{1..20}.png’: No such file or directory
But there is already 1.png to 20.png image file + Folder...
So how to move sequence file like
{1..20}.png -> Folder 1
{21..40}.png -> Folder 2
Thank you!!!
I don't think that it is possible to combine brace expansion with arithmetic expressions as you are doing. Specifically, ranges like {a..b} must contain literal values, not variables.
I would suggest that instead, you used a for loop:
for ((n=j*20-19;n<=j*20;++n)); do mv "$n.png" "$i"; done
The disadvantage of the above approach is that mv is called many times, rather than once. As suggested in the comments (thanks chepner), you could use an array to reduce the number of calls:
files=()
for ((n=j*20-19;n<=j*20;++n)); do files+=( "$n.png" ); done
mv "${files[#]}" "$i"
"${files[#]}" is the full contents of the array, so all of the files are moved in one call to mv.
You have to evaluate the resulting string again with eval
For example
eval "mv {$(( $j*20 -19))..$(( $j*20 ))}.png folder$j"

Bash Script to replicate files

I have 25 files in a directory. I need to amass 25000 files for testing purposes. I thought I could just replicate these files over and over until I get 25000 files. I could manually copy paste 1000 times but that seemed tedious. So I thought I could write a script to do it for me. I tried
cp * .
As a trial but I got an error that said the source and destination file are the same. If I were to automate it how would i do it so that each of the 1000 times the new files are made with unique names?
As discussed in the comments, you can do something like this:
for file in *
do
filename="${file%.*}" # get everything up to last dot
extension="${file##*.}" # get extension (text after last dot)
for i in {00001..10000}
do
cp $file ${filename}${i}${extension}
done
done
The trick for i in {00001..10000} is used to loop from 1 to 10000 having the number with leading zeros.
The ${filename}${i}${extension} is the same as $filename$i$extension but makes more clarity over what is a variable name and what is text. This way, you can also do ${filename}_${i}${extension} to get files like a_23.txt, etc.
In case your current files match a specific pattern, you can always do for file in a* (if they all are on the a + something format).
If you want to keep the extension of the files, you can use this. Assuming, you want to copy all txt-files:
#!/bin/bash
for f in *.txt
do
for i in {1..10000}
do
cp "$f" "${f%.*}_${i}.${f##*.}"
done
done
You could try this:
for file in *; do for i in {1..1000}; do cp $file $file-$i; done; done;
It will append a number to any existing files.
The next script
for file in *.*
do
eval $(sed 's/\(.*\)\.\([^\.]*\)$/base="\1";ext="\2";/' <<< "$file")
for n in {1..1000}
do
echo cp "$file" "$base-$n.$ext"
done
done
will:
take all files with extensions *.*
creates the basename and extension (sed)
in a cycle 1000 times copyes the original file to file-number.extension
it is for DRY-RUN, remove the echo if satisfied

Output from a bash script looping through multiple directories

I am currently trying to write a script that will loop through multiple directories. The main raw_data directory contains ~150 subdirectories (subj001, subj002,...,subj00n), each of which has several subdirectories.
How can I make sure that the output from the script given bellow will be sent back to the specific subdirectory (e.g. subj0012) the input was taken from, rather than the current directory (raw_data)?
#!/bin/bash
for dir in ~raw_data/*
do
tractor -d -r -b preproc RunStages:1
done
Thank you.
The name of the dir you want to save the output to is in $dir, right? So, just send the output there via redirection:
#!/bin/bash
for dir in ~raw_data/* ; do
tractor -d -r- b preproc RunStages:1 > $dir/output
done
You should make sure that what you are processing really is a directory, though.
You can use output of find to run a loop and append the output at desired location like this:
while read d
do
echo $d >> ~raw_data/subj0012/output
done < <(find ~raw_data -type d)

Resources