Interacting with files from multiple directories via bash script
You already accepted the answer of @Charles Duffy but (if I understood well) your question is about having files in different directories so if you need to work with multiple csv files on multiple directories you can use the following snippet:
# array containing the different directories to work with
pathDir=("/foo/bar/dir1" "/buzz/fizz/dir2")
for dir in "${pathDir[@]}" # For each directory
do
for file in "$dir/"*.csv; do # For each csv file of the directory
if [[ $(file -i "$file") == "$file: $charset" ]]; then
iconv -f ISO-8859-1 -t UTF-8 "$file" > "$file.new";
mv -f "$file.new" "$file";
fi
done
done
The pathDir
variable is an array which contains the path of different directories.
The first for
loop iterate through this array to get all the paths to check.
The second for
loop as in the previous answer iterate through the files of the current tested directory.
Using bash to loop through nested folders to run script in current working directory
Another, perhaps more flexible, approach to the problem is to use the find
command with the -exec
option to run a short "helper-script" for each file found below a directory path that ends in ".7"
. The -name
option allows find
to locate all files ending in ".7"
below a given directory using simple file-globbing (wildcards). The helper-script then performs the same operation on each file found by find
and handles moving the result.h5
to the proper directory.
The form of the command will be:
find /path/to/search -type f -name "*.7" -exec /path/to/helper-script '{}` \;
Where the -f
option tells find
to only return files (not directories) ending in ".7"
. Your helper-script
needs to be executable (e.g. chmod +x helper-script
) and unless it is in your PATH
, you must provide the full path to the script in the find command. The '{}'
will be replaced by the filename (including relative path) and passed as an argument to your helper-script
. The \;
simply terminates the command executed by -exec
.
(note there is another form for -exec
called -execdir
and another terminator '+'
that can be used to process the command on all files in a given directory -- that is a bit safer, but has additional PATH
requirements for the command being run. Since you have only one ".7"
file per-directory -- there isn't much benefit here)
The helper-script
just does what you need to do in each directory. Based on your description it could be something like the following:
#!/bin/bash
dir="${1%/*}" ## trim file.7 from end of path
cd "$dir" || { ## change to directory or handle error
printf "unable to change to directory %s\n" "$dir" >&2
exit 1
}
destdir="/Result_Folder/$dir" ## set destination dir for result.h5
mkdir -p "$destdir" || { ## create with all parent dirs or exit
printf "unable to create directory %s\n" "$dir" >&2
exit 1
}
ls *.pc 2>/dev/null || exit 1 ## check .pc file exists or exit
file7="${1##*/}" ## trim path from file.7 name
pc_script -f "$file7" -flags1 -other_flags ## first run
## check result.h5 exists and non-empty and copy to destdir
[ -s "result.h5" ] && cp -a "result.h5" "$destdir/new_result1.h5"
pc_script -f "$file7" -flags2 -other_flags ## second run
## check result.h5 exists and non-empty and copy to destdir
[ -s "result.h5" ] && cp -a "result.h5" "$destdir/new_result2.h5"
Which essentially stores the path part of the file.7
argument in dir
and changes to that directory. If unable to change to the directory (due to read-permissions, etc..) the error is handled and the script exits. Next the full directory structure is created below your Result_Folder
with mkdir -p
with the same error handling if the directory cannot be created.
ls
is used as a simple check to verify that a file ending in ".pc"
exits in that directory. There are other ways to do this by piping the results to wc -l
, but that spawns additional subshells that are best avoided.
(also note that Linux and Mac have files ending in ".pc"
for use by pkg-config
used when building programs from source -- they should not conflict with your files -- but be aware they exists in case you start chasing why weird ".pc"
files are found)
After all tests are performed, the path is trimmed from the current ".7"
filename storing just the filename in file7
. The file7
variabli is then used in your pc_script
command (which should also include the full path to the script if not in you PATH
). After the pc_script
is run [ -s "result.h5" ]
is used to verify that result.h5
exists and is non-empty before moving that file to your Result_Folder
location.
That should get you started. Using find
to locate all .7
files is a simple way to let the tool designed to find the files for you do its job -- rather than trying to hand-roll your own solution. That way you only have to concentrate on what should be done for each file found. (note: I don't have pc_script
or the files, so I have not testes this end-to-end, but it should be very close if not right-on-the-money)
There is nothing wrong in writing your own routine, but using find
eliminates a lot of area where bugs can hide in your own solution.
Let me know if you have further questions.
BASH Script for creating multiple directories, moving files, and then renaming said files
An alternative would be to create symlinks to the original files.
As you said before, each ZMAT symlink would need to be in its own directory.
The upside is that the original data doesn't move, so less risk of breaking it, but the tool you want to use should read the symlinks as if they are the files it is looking for.
This one-liner creates an out directory in the current folder that you could subsequently move wherever you want it. You could easily create it where you do want it by replacing "out" with whatever absolute path you wanted
for i in *.ZMAT; do mkdir -p out/$i ; ln -s $PWD/$i out/$i/ZMAT ; done
Script working on all files in various subdirectories
One solution would use find
and xargs
:
find /home/working_dir_with_subdirs/ -type f -print0 |
xargs -0 -n1 /opt/my_script -e -x
This would call /opt/my_script
once for each file located in /home/working_dir_with_subdirs
and its subdirectories.
If your script can accept multiple files as arguments, you could drop the -n1
:
find /home/working_dir_with_subdirs/ -type f -print0 |
xargs -0 /opt/my_script -e -x
This would call your script with multiple files as arguments, rather than once per file.
In both cases, the -print0
on find
and the -0
on xargs
are to correctly handle filenames that contain whitespace.
How to move all the files of a directory into multiple directories with a given number of files?
The following should work:
dest_base="destination"
src_dir="src/"
filesperdir=500
atfile=0
atdir=0
for file in $src_dir/*; do
if ((atfile == 0)); then
dest_dir=$(printf "$dest_base/%0.5d" $atdir)
[[ -d $dest_dir ]] || mkdir -p $dest_dir
fi
mv $file $dest_dir
((atfile++))
if ((atfile >= filesperdir)); then
atfile=0
((atdir++))
fi
done
cd to multiple directories in a shell script
You cannot cd
to more than one directory at a time (so you need to come back to the original directory, if the path to cd
is relative). It is not a limitation of the shell but a property of unix processes (so coding in some other language like Python or C won't change much); they each have only
one current directory (and the shell runs the chdir(2) system call for its cd
builtin)
You might consider computing the full path (using realpath(1)) of every directory, and storing that path in some shell variable. Then you could cd
in a loop.
Some shells have a stack of directories, which you could manipulate with pushd
& popd
. In that case, you might code
for d in */* ; do
pushd "$d"
sh small_script
popd
done
You need to be sure that the globbing expansion of */*
gives only directories. With zsh
you might code */*(/)
to expand only directories. See glob(7) & path_resolution(7) & credentials(7).
If your shell don't have pushd
you could try
for d in $(realpath */*); do
cd "$d" && sh small_script.sh
done
or using a subshell
for d in */*; do
(cd "$d" && exec small_script.sh)
done
See this for more about pushd
& popd
Notice that if you do cd a/b
followed later by cd e/f
the shell is trying to go (for the second cd
) into the a/b/e/f
directory (which probably do not exist) because e/f
is a relative path (so interpreted in the context of the previous current directory a/b
). You might want cd a/b
followed by cd ../../e/f
...
Take time to understand why cd
needs to be a shell builtin. Perhaps read Advanced Linux Programming.
See also find(1). It might be more useful (with its -exec
action).
BTW, it could be simpler to change your small_script
to accept as argument the directory in which it should go. Then that script might start with cd "$1"
.
Edit certain files in multiple directories using a shell script
Can you use find to match the user files ?
e.g.
find ./ -maxdepth 1 -name "user*" -exec script_that_does_text_replace.sh {\} \;
where script_that_does_text_replace.sh
is the code you wrote above.
Perform an action in every sub-directory using Bash
for D in `find . -type d`
do
//Do whatever you need with D
done
list all files in multiple directories
You can list file with full path of a given directory using printf
:
printf "$PWD%s\n" user.newskims.131017222/*
Related Topics
Bash/Linux Sort by 3Rd Column Using Custom Field Seperator
How to Start/Restart/Stop Apache Server on Linux as Non-Root User
How to Execute 32-Bit Code in 64-Bit Process by Doing Mode-Switching
How to Get the Output of Ansible Ad-Hoc Command in JSON, CSV or Other Format
How to Find Files with Same Size
Where Is $Path Set? Specifically Where Is My MAC Port Path Being Set
Rsync, 'Uid/Gid Impossible to Set' Cases Cause Future Hard Link Failure, How to Fix
Why Fftw on Windows Is Faster Than on Linux
File Glob Patterns in Linux Terminal
"Command Not Found" Piping a Variable to Cut When Output Stored in a Variable
Bash: Checking If Files Are Duplicates Within a Directory
How to Add Output "Non_Assigned" When There Is No Match in Grep
Segmentation Fault with a Variable in Section .Data
Determine Os from a Single Command Line Operation
No Console Output Available on Linux When Executing Grails/Groovy
Linux Command Line: How Can Simply Feed Arbitrary Strings to Pipe