I need some slightly basic BASH help - find + execdir + sox - bash

Ok, hello. I have some scripts that I use on the command line to rip mp3s from .wav and .aif files. Lately I've been playing with this sampler that wants only mono wav files. WAIT - I promise this is a programming question.
I want to run a command to split all wav files inside a directory to mono files with a different file name.
So, for example Drummerrolleasy.wav, would then have DrummerrolleasyL.wav in the same directory as the left mono split of the original stereo wav.
I will ensure that when the command runs, there are only stereo wav files in the directory.
I have this command:
find . -name "*.wav" -execdir echo $1 {} \;
that works as expected, and provides me the names of the files, one by one, that I would like to then put inside a call to sox.
this:
find . -type f -name “*.wav” -exec sox {} "$1" L"$1" \;
does not work (runs but does nothing), and I'm not finding anything particularly illuminating for how to do this. I might need to spawn a new shell, but this doesn't work either because the shell just sits there:
find . -name "*.wav" -execdir sh -c 'sox "{}" "$1" “L””$1"’ \;
If I have to figure this out on my own, that can work too, and when I get there I will update but if someone would like to point me in the right direction I would be much obliged.

Goes like this:
for file in *.wav; do sox $file L$file remix 1; done
thanks Mix .L and .R files into a stereo file using SOX in bulk
saves me a lot of time. BASH rules.

Related

shell Automator script to re-encode MP3 using LAME and overwriting original files

I'm looking to run through a parent folder that has many sub folders of MP3 files and re-encode at a lower sample and bit rate. I'm doing this in Automator on my Mac using a Run Shell Script workflow and making it a Finder action.
Currenlty I can pass a folder and the script works but I'm getting new MP3 files with a double *.mp3.mp3 extension.
I know LAME cannot overwrite existing files, but I'm having trouble creating the command that would delete the original files when complete, then remove the double .mp3 extension off of the new files.
find "$1" -name "*.mp3" -execdir /usr/local/bin/lame -a --resample 44.1 -b 48 {} \;
I came across this lame - Overwrite existing files but cannot get it to work within Automator.
If you want to do extra logic, like deleting files, make find launch a shell to do it in.
find "$1" -name "*.mp3" -execdir /bin/sh -c '
for arg in "$#"; do
if /usr/local/bin/lame -a --resample 44.1 -b 48 "$arg"; then
mv -- "$arg.mp3" "$arg" # success
else
rm -- "$arg.mp3" # failure
fi
done
' {} +

Shell script for finding (and deleting) video files if they came from a rar

My download program automatically unrars rar archives, which is all well and good as Sonarr and Radarr need that original video file to import. But now my download HDD fills up with all these video files I no longer need.
I've tried playing around with modifying existing scripts I have, but every step seems to take me further from the goal.
Here's what I have so far (that isnt working and I clearly dont know what im doing). My main problem is I can't get it to find the files correctly yet. This script jumps right to "no files found". So I'm doing the search wrong at the very least. Or I'm pretty sure I might need to completely rewrite from scratch using a different method I'm not aware of..
#!/bin/bash
# Find video files and if it came from a rar, remove it.
# If no directory is given, work in local dir
if [ "$1" = "" ]; then
DIR="."
else
DIR="$1"
fi
# Find all the MKV files in this dir and its subdirs
find "$DIR" -type f -name '*.mkv' | while read filename
do
# If video file and rar file exists, delete mkv.
for f in ...
do
if [[ -f "$DIR/*.mkv" ]] && [[ -f "$DIR/*.rar" ]]
then
# rm $filename
printf "[Dry run delete]: $filename\n"
else
printf "No files found\n"
exit 1
fi
done
Example of directory structure before and after. Note the file names are often different to the extracted file. And I want to leave other folders that don't have rars in them alone.
Before:
/folder/moviename/Movie.that.came.from.rar.2021.dvdrip.mkv
/folder/moviename/movie.rar
/folder/moviename/movie.r00
/folder/moviename/movie.r01
/folder/moviename2/Movie.that.lives.alone.2021.dvdrip.mkv
/folder/moviename2/Movie.2021.dvdrip.nfo
After
# (deleted the mkv only from the first folder)
/folder/moviename/movie.rar
/folder/moviename/movie.r00
/folder/moviename/movie.r01
# (this mkv survives)
/folder/moviename2/Movie.that.lives.alone.2021.dvdrip.mkv
/folder/moviename2/Movie.2021.dvdrip.nfo
TL:DR I would like a script to look recursively in my download drive for video files and rar files, and if it sees both in the same folder, delete the video file.
With GNU find, you can condense this to one command:
find "${1:-.}" -type f -name '*.rar' -execdir sh -c 'echo rm *.mkv' \;
${1:-.} says "use $1, or . if $1 is undefined or empty".
For each .rar file found, this starts a new shell in the directory of the file found (that's what -execdir sh -c '...' does) and runs echo rm *.mkv.
If the list of files to delete looks correct, you can actually delete them by dropping the echo:
find "${1:-.}" -type f -name '*.rar' -execdir sh -c 'rm *.mkv' \;
Two remarks, though:
-execdir rm *.mkv \; would be shorter, but then the glob might be expanded prematurely in case there are .mkv files in the current directory
if a directory contains a .rar file, but no .mkv, this will try to delete a file called literally *.mkv and cause an error message

Batch convert PNGs to individual PDFs while maintaining deep folder hierarchy in bash

I've found a solution that claims to do one folder, but I have a deep folder hierarchy of sheet music that I'd like to batch convert from png to pdf. What do my solutions look like?
I will run into a further problem down the line, which may complicate things. Maybe I should write a script? (I'm a total n00b fyi)
The "further problem" is that some of my sheet music spans more than one page, so if the script can parse filenames that include "1of2" and "2of2" to be turned into a single pdf, that'd be neat.
What are my options here?
Thank you so much.
Updated Answer
As an alternative, the following should be faster (as it does the conversions in parallel) and also able to handle larger numbers of files:
find . -name \*.png -print0 | parallel -0 convert {} {.}.pdf
It uses GNU Parallel which is readily available on Linux/Unix and which can be simply installed on OSX with homebrew using:
brew install parallel
Original Answer (as accepted)
If you have bash version 4 or better, you can use extended globbing to recurse directories and do your job very simply:
First enable extended globbing with:
shopt -s globstar
Then recursively convert PNGs to PDFs:
mogrify -format pdf **/*.png
You can loop over png files in a folder hierarchy, and process each one as follows:
find /path/to/your/files -name '*.png' |
while read -r f; do
g=$(basename "$f" .png).pdf
your_conversion_program <"$f" >"$g"
done
To merge pdf-s, you could use pdftk. You need to find all pdf files that have a 1of2 and 2of2 in their name, and run pdftk on those:
find /path/to/your/files -name '*1of2*.pdf' |
while read -r f1; do
f2=${f1/1of2/2of2} # name of second file
([ -f "$f1" ] && [ -f "$f2" ]) || continue # check both exist
g=${f1/1of2//} # name of output file
(! [ -f "$g" ]) || continue # if output exists, skip
pdftk "$f1" "$f2" output "$g"
done
See:
bash string substitution
Regarding a deep folder hierarchy you may use find with -exec option.
First you find all the PNGs in every subfolder and convert them to PDF:
find ./ -name \*\.png -exec convert {} {}.pdf \;
You'll get new PDF files with extension ".png.pdf" (image.png would be converted to image.png.pdf for example)
To correct extensions you may run find command again but this time with "rename" after -exec option.
find ./ -name \*\.png\.pdf -exec rename s/\.png\.pdf/\.pdf/ {} \;
If you want to delete source PNG files, you may use this command, which deletes all files with ".png" extension recursively in every subfolder:
find ./ -name \*\.png -exec rm {} \;
if i understand :
you want to concatenate all your png files from a deep folders structure into only one single pdf.
so...
insure you png are ordered as you want in your folders
be aware you can redirect output of a command (say a search one ;) ) to the input of convert, and tell convert to output in one pdf.
General syntax of convert :
convert 1.png 2.png ... global_png.pdf
The following command :
convert `find . -name '*'.png -print` global_png.pdf
searches for png files in folders from cur_dir
redirects the output of the command find to the input of convert, this is done by back quoting find command
converts works and output to pdf file
(this very simple command line works fine only with unspaced filenames, don't miss quoting the wild char, and back quoting the find command ;) )
[edit]Care....
be sure of what you are doing.
if you delete your png files, you will just loose your original sources...
it might be a very bad practice...
using convert without any tricky -quality output option could create an enormous pdf file... and you might have to re-convert with -quality "60" for instance...
so keep your original sources until you do not need them any more

Find/Match variable filenames and move files to respective directory

I've never come to SO asking "Do my homework" but I really don't know where to start with this one.
I have a load of documents which are dumped in a directory after being auto-signed using JSignPdf (--output-directory option seemingly has no ability to output to same as input):
/some/dir/Signed/PDF1_signed.pdf
/some/dir/Signed/PDF2_signed.pdf
/some/dir/Signed/PDF2_signed.pdf
I'd like to then find their source/unsigned counterparts:
/some/dir/with/docs/PDF1.pdf
/some/dir/where/is/PDF2.pdf
/some/dir/why/this/PDF3.pdf
...and move the signed PDFs into the respective directories.
I use the command, to find all the PDFs in the variety of directories:
find . -name '*.pdf' -exec sh -c 'exec java -jar jsignpdf-1.4.3/JSignPdf.jar ... ' sh {} +
...and I've tried things like making find output a variable and then using IF THEN to match with no success. Would I need find output to be made into multiple variables? I'm so lost :(
I'd like to accomplish this in some shell, but if there are Perl junkies out there or anything else, I am more than happy for another portable solution.
I've tried to break it down, but still don't understand how to accomplish it...
find files matching VarName without _signed
move _signed file with matching name to the directory of found file
Thanks for any help/guidance.
Use a while loop to read each file found by find and move it to the correct place:
find /some/dir -name "*.pdf" ! -name "*_signed.pdf" -print0 | while IFS= read -d '' -r file
do
f="${file##*/}"
mv "/some/dir/Signed/${f%.*}_signed.pdf" "${file%/*}"
done
I have a similar problem I've been working on. Since the path manipulation required to convert /some/dir/where/is/PDF2.pdf to /some/dir/Signed/PDF2_signed.pdf is fairly simple but more involved than can be done in a simple one-liner, I've been using find to locate the first set, and using a simple loop to process them one at a time. You did mention homework, so I'll try not to give you too much code.
find /dir/containing/unsigned -name '*.pdf' -print0 | while IFS= read -d path; do
fetch_signed_version "$path"
done
where fetch_signed_version is a shell function you write that, given a path such as /some/dir/where/is/PDF2.pdf, extracts the directory (/some/dir/where/is), computes the signed PDF's name (PDF2_signed.pdf), then executes the necessary move (mv /some/dir/Signed/$signed_pdf /some/dir/where/is)
fetch_signed_version is actually pretty simple:
fetch_signed_version () {
dir=${1%/*}
fname=${1##*/}
signed_name=${fname%.pdf}_signed.pdf
mv "/some/dir/Signed/$signed_name" "$dir"
}

How to recursivly use JpegTran (command line) to optimise all files in subdirs?

i have Photos in multiple directories. I want to use jpegtran (command line tool) to recursivly go through each one, optimise it, and save it (overwrite it)
if they are all in one folder i use this
for JPEG in *.jpg ; do jpegtran -optimize $JPEG > $JPEG; done
but i can't get it working recursivly and overwriting the same file (rather than to a new filename)
any tips?
jpegtran whole directory
find /the/image/path -name "*.jpg" -type f -exec jpegtran -copy none -optimize -outfile {} {} \;
How about using the find command:
find /your/dir -name '*.jpg' -exec echo jpegtran -optimize {} \;
Run the command, if you like the output, remove echo to execute it.
It is usually a whole lot easier to use find in such cases, because what you want to do is act on a few filenames. The find utility will give you those names. Assuming you have GNU (or maybe even BSD) tools, the following example will illustrate this common scenario.
For example:
$ find ~/images/wallpapers/TEMP/ -type f -iname '*jpg' \
-exec sh -c 'jpegtran -outfile {}.out -optimize {}; mv {}.out {} ' \;
find looks for all files ending with jpg in the TEMP folder, recursively.
For every full file path found (denoted by `{}'), find will run the command given to -exec
the -exec option cheats and runs several commands instead of one through sh
Notes
cat file > file is not allowed because simultaneously reading from, and writing to the same file is not a well defined or even supported operation in bash.
Talking about Python approach:
Firstly make an list of all of your paths.
Then:
import subprocess
for path in paths:
for image_name in arr:
subprocess.call(["jpegtran", "-your", "-instructions"])
Let's say your instruction is something like:
jpegtran -optimize -progressive -scans script_new.txt -outfile progressive.jpg original.jpg
So your instruction will be like:
subprocess.call(["jpegtran", "-optimize", "-progressive", "-scans", f"{scan_name}.txt", "-outfile", name_progressive, name_original])
For your convenience, name_progressive, name_original and scan_name are variables and concept f-string is used.

Resources