How to gzip all files in all sub-directories in bash - linux

I want to iterate among sub directories of my current location and gzip each file seperately. For zipping files in a directory, I use
for file in *; do gzip "$file"; done
but this can just work on current directory and not the sub directories of the current directory. How can I rewrite the above statements so that It also zips the files in all subdirectories?

I'd prefer gzip -r ./ which does the same thing but is shorter.

No need for loops or anything more than find and gzip:
find . -type f ! -name '*.gz' -exec gzip "{}" \;
This finds all regular files in and below the current directory whose names don't end with the .gz extension (that is, all files that are not already compressed). It invokes gzip on each file individually.
Edit, based on comment from user unknown:
The curly braces ({}) are replaced with the filename, which is passed directly, as a single word, to the command following -exec as you can see here:
$ touch foo
$ touch "bar baz"
$ touch xyzzy
$ find . -exec echo {} \;
./foo
./bar baz
./xyzzy

find . -type f | while read file; do gzip "$file"; done

I can't comment on the top post (yet...), but I read in the man pages of "find" that -execDir is safer than -exec because the command is done in the subdirectory where the match is found, rather than the parent directory where "find" is ran from.
If anyone would like to use a regex with to locate specific files in a subdirectory to zip, I'd recommend using
find ./ -type f -name 'addRegexHere' -execdir gzip -k "{}" \;
if you don't need regex's, stick with the recursive gzip call above (or below, if I gain any traction haha)
source

Related

Loop through a directory with any level of depth

I want to execute a command on all files present on all levels in the directory. It may have any number of files and sub directories. Even these sub directories may contain any number of files and subdirectories. I want to do this using shell script. As I am new to this field can any one suggest me a way out.
You can use the command "find" with "xargs" after "|"(pipe).
Example: Suppose that I want to remove all files that have ".txt" extension on "Documents" directory:
find Documents -iname *.txt |xargs rm -f
Helps?
You can use a recursive command that uses wildcard characters (*) like so:
for dir in ~/dev/myproject/*; do (cd "$dir" && git status); done
If you want to apply commands on the individual files you should use the find command and execute commands on it like so:
find yourdirectory -type f -exec echo "File found: '{}'" \;
What this does:
finds all the items in the directory yourdirectory
that have the type f - so are a file
runs an exec on each file
Use find:
find -type f -exec COMMAND {} \;
-f applies the command only to files, not to directories. The command is recursive by default.

Linux rename files as dirname

i got lots of files like this:
./1/wwuhw.mp3
./2/nweiewe.mp3
./3/iwqjoiw.mp3
./4/ncionw.MP3
./5/joiwqfm.wmv
./6/jqoifiew.WMV
how can i rename them like this in Linux Bash:
./1/1.mp3
./2/2.mp3
./3/3.mp3
./4/4.MP3
./5/5.wmv
./6/6.WMV
Try this,
for i in */*; do mv $i $(dirname $i)/$(dirname $i).${i##*.}; done
For loop iterates over each file in directory one by one. and mv statement renames the each file in directory one by one.
Something like this should do the job:
for i in */*; do
echo mv "${i}" "${i%/*}/${i%/*}.${i##*.}"
done
See e.g. here, what this cryptic parameter expansions (like ${i%/*}) mean in bash.
The script above will only print the commands in the console, without invoking them. Once you are sure you want to proceed, you can remove the echo statement and let it run.
If you don't mind using external tool, then rnm can do this pretty easily:
rnm -ns '/pd0/./e/' */*
/pd0/ is the immediate parent directory, /pd1/ is the directory before that and so forth.
-ns means name string and /pd/ and /e/ are name string rules which expands to parent directory and file extension respectively.
The general format of the /pd/ rule is /pd<digit>-<digit>-<delim>/, for example, a rule like /pd0-2-_/ will construct dir0_dir1_dir2 from a directory structure of dir2/dir1/dir0
More examples can be found here.
The for loop method, as outlined in some of the other answers, would suffice and work great for most cases where you need to rename every file in a directory to the first parent's directory name. My particular case called for a bit more granularity, where I only wanted to rename a subset of the files in a directory and assert that the operand was, in fact, an actual file, not an empty directory, symbolic link, etc. Using find can achieve exactly what you want in addition to the added ability to apply filtration and processing to the file inputs and outputs.
#####################################
# Same effect as using a `for` loop #
#####################################
#
# -mindepth 2 : ensures that the file has a parent directory.
# -type f : ensures that we are working with a `regular file` (not directory, symlink, etc.).
find . -mindepth 2 -type f -exec bash -c 'file="{}"; dir="$(dirname $file)"; mv "$file" "$dir/${dir##*/}.${file##*.}"' \;
#########################
# Additional filtration #
#########################
# mp3 ONLY (case insensitive)
find . -mindepth 2 -type f -iname "*.mp3" -exec bash -c 'file="{}"; dir="$(dirname $file)"; mv "$file" "$dir/${dir##*/}.${file##*.}"' \;
# mp3 OR mp4 ONLY (case insensitive)
find . -mindepth 2 -type f \( -iname "*.mp3" -or -iname "*.mp4" \) -exec bash -c 'file="{}"; "dir=$(dirname $file)"; mv "$file" "$dir/${dir##*/}.${file##*.}"' \;

Linux recursive copy files to its parent folder

I want to copy recursively files to its parent folder for a specific file extension. For example:
./folderA/folder1/*.txt to ./folderA/*.txt
./folderB/folder2/*.txt to ./folderB/*.txt
etc.
I checked cp and find commands but couldn't get it working.
I suspect that while you say copy, you actually mean to move the files up to their respective parent directories. It can be done easily using find:
$ find . -name '*.txt' -type f -execdir mv -n '{}' ../ \;
The above command recurses into the current directory . and then applies the following cascade of conditionals to each item found:
-name '*.txt' will filter out only files that have the .txt extension
-type f will filter out only regular files (eg, not directories that – for whatever reason – happen to have a name ending in .txt)
-execdir mv -n '{}' ../ \; executes the command mv -n '{}' ../ in the containing directory where the {} is a placeholder for the matched file's name and the single quotes are needed to stop the shell from interpreting the curly braces. The ; terminates the command and again has to be escaped from the shell interpreting it.
I have passed the -n flag to the mv program to avoid accidentally overwriting an existing file.
The above command will transform the following file system tree
dir1/
dir11/
file3.txt
file4.txt
dir12/
file2.txt
dir2/
dir21/
file6.dat
dir22/
dir221/
dir221/file8.txt
file7.txt
file5.txt
dir3/
file9.dat
file1.txt
into this one:
dir1/
dir11/
dir12/
file3.txt
file4.txt
dir2/
dir21/
file6.dat
dir22/
dir221/
file8.txt
file7.txt
dir3/
file9.dat
file2.txt
file5.txt
To get rid of the empty directories, run
$ find . -type d -empty -delete
Again, this command will traverse the current directory . and then apply the following:
-type d this time filters out only directories
-empty filters out only those that are empty
-delete deletes them.
Fine print: -execdir is not specified by POSIX, though major implementations (at least the GNU and BSD one) support it. If you need strict POSIX compliance, you'll have to make do with the less safe -exec which would need additional thought to be applied correctly in this case.
Finally, please try your commands in a test directory with dummy files, not your actual data. Especially with the -delete option of find, you can loose all your data quicker than you might imaging. Read the man page and, if that is not enough, the reference manual of find. Never blindly copy shell commands from random strangers posted on the internet if you don't understand them.
$cp ./folderA/folder1/*.txt ./folderA
Try this commnad
Run something like this from the root(ish) directory:
#! /bin/bash
BASE_DIR=./
new_dir() {
LOC_DIR=`pwd`
for i in "${LOC_DIR}"/*; do
[[ -f "${i}" ]] && cp "${i}" ../
[[ -d "${i}" ]] && cd "${i}" && new_dir
cd ..
done
return 0
}
new_dir
This will search each directory. When a file is encountered, it copies the file up a directory. When a directory is found, it will move down into the directory and start the process over again. I think it'll work for you.
Good luck.

Search for text files in a directory and append a (static) line to each of them

I have a directory with many subdirectories and files with suffixes in those subdirectories (e.g FileA-suffixA FileB-SuffixB FileC-SuffixC FileD-SuffixA, etc).
How can I recursively search for files with a certain suffix, and append a user-defined line of text to those files? I feel like this is a job for grep and sed, but I'm not sure how I would go about doing it. I'm fairly new to scripting, so please bear with me.
You can do it like
find /where/to/search -type f -iname '*.SUFFIX' -exec echo "USER DEFINED STRING" >> \{\} \;
find searches in the suplied path
-type f finds only files
-iname '*.SUFFIX' find the .SUFFIXed names, case ignored
find ./ -name "*suffix" -exec bash -c 'echo "line_to_add" >> $1' -- {} \;
Basically you use find to get a list of the files. Then you use bash to echo append your line to that list.

Changing all file's extensions in a folder using CLI in Linux

How to change all file's extensions in a folder using one command on CLI in Linux?
Use rename:
rename 's/.old$/.new/' *.old
If you have the perl rename installed (there are different rename implementations) you can do something like this:
$ ls -1
test1.foo
test2.foo
test3.foo
$ rename 's/\.foo$/.bar/' *.foo
$ ls -1
test1.bar
test2.bar
test3.bar
You could use a for-loop on the command line:
for foo in *.old; do mv $foo `basename $foo .old`.new; done
this will take all files with extension .old and rename them to .new
This should works on current directory AND sub-directories. It will rename all .oldExtension files under directory structure with a new extension.
for f in `find . -iname '*.oldExtension' -type f -print`;do mv "$f" ${f%.oldExtension}.newExtension; done
This will work recursively, and with files containing spaces.
Be sure to replace .old and .new with the proper extensions before running.
find . -iname '*.old' -type f -exec bash -c 'mv "$0" "${0%.old}.new"' {} \;
Source : recursively add file extension to all files
(Not my answer.)
find . -type f -exec mv '{}' '{}'.jpg \;
Explanation: this recursively finds all files (-type f) starting from the current directory (.) and applies the move command (mv) to each of them. Note also the quotes around {}, so that filenames with spaces (and even newlines...) are properly handled.

Resources