I have hundreds of image files that are currently named:
[LASTname], [firstname].jpg
I need to rename them all:
[firstname]_[LASTname].jpg
So I can't do a simple search and replace or sequential convention or anything like that. I need to copy what comes after the comma, paste it to the front and replace the ", " with a "_"
I am very new to applescript, but it appears that might be a solution. Does any have any ideas on how I could accomplish this?
I would make a backup first, then do this on a spare COPY of your files in a separate directory.
Save this in your HOME directory as go
#!/bin/bash
shopt -s nullglob nocaseglob
for f in *,*.jpg; do
base=${f/.*/} # strip extension
last=${base/,*/} # remove comma and anything after
first=${base/*,/} # remove anything up to and including comma
echo mv "$f" "${first}_${last}.jpg"
done
Now start Terminal and make the script executable with:
chmod +x go
Now change directory to where your images are, so if they are in your Desktop in a folder called COPY
cd Desktop/COPY
Then run the script with:
$HOME/go
If the commands look correct, edit the script and remove the word echo near the end and run it again, for real.
By the way, mv is the command to rename a file, so the following changes fileA's name to fileB:
mv fileA fileB
Related
I have a script that, when I right click on a folder, combines all pngs/jpgs/tifs inside the folder into a PDF and renames the PDF to the name of the folder it resides in.
cd %~dpnx1
for %%a in (.) do set currentfolder=%%~na
start cmd /k magick "*.{png,jpg,tif}" "%currentfolder%.pdf"
However, I have quite a lot of folders and currently have to do this one by one.
How can I create a function where I can right click on a folder, which searches subfolders and combines the jpgs to PDF?
So in the example below, Im wanting to create 3 PDFS (Folder A, Folder B and Folder C) by right clicking and running batch on the parent folder.
Example:
Parent folder (one that I would right click and run script from)
|- Folder A
||- test1.jpg
||- test2.jpg
||- test3.jpg
|- Folder B
||- example1.jpg
|| - example2.jpg
|- Folder C
|| Folder D
|||- temp.jpg
|||- temp2.jpg
I have also recently moved to Mac so I'm looking to use zsh. I've had some help to attempt to use the following myself but no luck:
#!/bin/bash
# Set the output directory
output_dir='./pdfs/'
# Make the output directory if it doesn't exist
mkdir -p "$output_dir"
# Check if an input directory was provided as a command-line argument
if [ $# -eq 0 ]
then
# Use the current directory as the input directory if none was provided
input_dir='./'
else
# Use the first command-line argument as the input directory
input_dir="$1"
fi
# Find all the directories in the input directory
find "$input_dir" -type d | while read dir; do
# Extract the base directory name
dirname=$(basename "$dir")
# Create a PDF file with the same name as the base directory name
output_file="$output_dir/$dirname.pdf"
# Find all the JPEG files in the current directory
find "$dir" -type f -name '*.jpg' | while read file; do
# Convert the JPEG file to PDF and append it to the output file
convert "$file" "$file.pdf"
done
# Concatenate all the PDF files in the current directory into a single PDF
gs -q -dNOPAUSE -dBATCH -sDEVICE=pdfwrite -sOutputFile="$output_file" "$dir"/*.pdf
# Remove the temporary PDF files
rm "$dir"/*.pdf
done
Hope you can help. Thank you.
There are several aspects to this question, and judging by your attempted solution, they will all be non-trivial for you. It is more than a normal question so I'll just give you an outline so you can tackle it in chunks. You'll need to:
install homebrew
install ImageMagick
use Automator to make a workflow for right-click
learn some bash scripting to recurse through directories
learn some ImageMagick to make PDFs
Install homebrew
Go to here and follow instructions to install homebrew. I am not repeating the instructions here as they may change.
You'll likely need to install Xcode command-line tools with:
xcode-select --install
You'll need to set your PATH properly afterwards. Don't omit this step.
Install ImageMagick
You'll need to do:
brew install imagemagick
Setup workflow with Automator for right-click
Next you need to make a script that will be executed when you right-click on a directory. It will look like this when we have done it. I right-clicked on the Junk directory on my desktop and went down to Quick Actions and across to makePDFs.
So, in order to do that you need to start the Automator by clicking ⌘ SPACE and typing Automator and hitting ENTER when it guesses.
Then select New Document and Quick Action. Now navigate the orange areas in the diagram till you find Run Shell Script then drag Run Shell Script over to the right side and drop it in the blue zone. Go on the Edit menu and click ⌘ and then Save As and enter makePDFs in the box. This is the name that will appear in future on your right-click menu.
Now set the options in the green box like I have done.
Now replace all the code in the blue box with the code copied from below:
#!/bin/bash
################################################################################
# Recurse into all subdirectories specified in parameter and make PDF in each
# directory of all images found in there.
################################################################################
# Add ImageMagick from homebrew to PATH
PATH=$PATH:/opt/homebrew/bin
# Check we got a directory as parameter
if [ $# -ne 1 ] ; then
>&2 echo "Usage: $0 DIRECTORY"
exit 1
fi
# Find and process all subdirectories
shopt -s nullglob
while read -rd $'\0' dir; do
# Start a subshell so we don't have to cd back somewhere
(
cd "$dir" || exit 1
# Make list of all images in directory
declare -a images
for image in *.jpg *.png *.tif ; do
images+=("$image")
done
numImages=${#images[#]}
if [ $numImages -gt 0 ] ; then
pdfname=${PWD##*/}
magick "${images[#]}" "${pdfname}.pdf"
fi
)
done < <(find "$1" -type d -print0)
Finally, set the options like I did in the cyan coloured box.
Now save the whole workflow again and everything should work nicely.
For bash, try this (tested on Linux):
for d in */; do convert "$d"/*.{png,jpg,tif} "$d/${d%/}.pdf" ; done
In slo-mo:
for d in */: loop on all directories in current directory (the / restrict matches to directories)). Variable $d contains the directory name, and name has a final /.
"$d"/*.{png,jpg,tif}: all files with png, jpg or tif extension in the directory "$d"
"$d/${d%/}.pdf": the directory name, a slash, the directory name with the ending slash removed, and .pdf`
If you look carefully, the explicit /s in the code aren't necessary since there is already one at the end of $d, but leaving them in makes the code a bit more readable and the multiple '//' are coalesced into a single one.
This code may however complain that there are no png/jpg/tif. A slightly different form makes it behave more nicely:
shopt -s extglob # this is possibly already set by default
for d in */; do convert "$d"/*.#(png|jpg|tif) "$d/${d%/}".pdf ; done
for zsh this could be (untested!):
# shopt -s extglob # no shopt necessary
for d in */; do convert "$d"/*.png(N) "$d"/*.jpg(N) "$d"/*.tif(N) "$d/${d%/}".pdf ; done
with the caveat that if no pattern matches, the command will just be convert whatever_output.pdf and you will get the built-in help.
The difference is that *.{png,jpg,tif} is expanded to *.png *.jpg *.tif before any pattern matching is done, so this represents three file patterns and the shell tries to match each pattern in turn (and leaves the literal pattern in case there is no match), while *.#(png|jpg|tif) is a single file pattern that matches any of the three extensions. This can also make a difference for you because the files do not appear in the same order, *.{png,jpg,tif} lists all the PNG, then the JPG,then the TIF, while *.#(png|jpg|tif) has them all sorted in alphabetical order without regard for the extension.
I have a txt file with a list of files (approximately 500) for example:
file_0_hard.msOut
file_1_hard.msOut
file_10_hard.msOut
.
.
.
file_1000_hard.msOut
I want to delete all those files whose name is not in the txt file. All of these files are in the same directory. How can I do this using bash where I read the text file and then delete all those files in the directory that are not in the text file. Help would be appreciated.
Along the lines of user1934428
There is something to say for this solution. But since we have linux at our disposal with a strong filesystem in use I hope. we can make hardlinks; The only requirement for that the destination is on the same filesystem.
So along those lines:
make a directory to store the files you want to keep.
hardlink (ln {file} {target}) ; as this does not cost extra disk space, it only stores the inode number in the new directory file.
remove all files
move the files back from their origin.
And actually this would be about the same as:
mv {files} {save spot}
remove all files
mv {save spot}/{files} back
Which does pretty much the same thing. Then again; it is a nice way to learn about the power of a hardlink.
you may try this :
cd path/dir
for f in *; do
if ! grep -Fxq "$f" pathToFile/file.txt; then
rm -r "$f"
else
printf "exists-- %s \n" ${f}
fi
done
In case you are wondering (as I did) what -Fxq means in plain English:
F: Affects how PATTERN is interpreted (fixed string instead of a regex)
x: Match whole line
q: Shhhhh... minimal printing
Assuming the directory in question is mydir
set -e
cd mydir
tmpdir=/tmp/x$$ # adapt this to your taste
mv $(<list.txt) $tmpdir
cd ..
rm -r mydir
mkdir mydir
mv $tmpdir/* mydir
rm -r $tmpdir
Basically, instead to delete those files you want to keep, you safe them, then delete everything, and then restore them. For your case, this is probably faster than doing the other way around.
UPDATE:
As Michiel commented, it is advisable that you place your tmpdir in the same file system as mydir.
Using Ubuntu 18.04. Say we have a file called debug.log. You can create a copy called debug_BACKUP.log with either of these commands:
cp debug.log debug_BACKUP.log
cp debug{,_BACKUP}.log
Alternatively, substitute cp with mv to rename the file.
Now suppose we have debug1.log and debug2.log. We would like to create copies called debug1_BACKUP.log and debug2_BACKUP.log. Is there a single command to achieve this?
When I tried either of the following:
cp debug*.log debug*_BACKUP.log
cp debug*{,_BACKUP}.log
the error is cp: target 'debug*_BACKUP.log' is not a directory.
Brace expansions are an instruction for the shell about how to rewrite your command before glob expansion takes place. They aren't passed to the command itself -- cp has no idea if a brace expansion was used. For that matter, cp doesn't even have any idea if a wildcard is used; when you run cp *.txt dir/, the shell generates an array of C strings corresponding to something like cp foo.txt bar.txt baz.txt dir/ before running it.
This means that if you want to rewrite content after wildcard expansion takes place, you need to do it by hand.
for f in debug*.log; do
[[ $f = *_BACKUP.log ]] && continue # skip things that are already backup files
cp "$f" "${f%.log}_BACKUP.log"
done
There are few excellent bulk rename programs, including Perl based file-rename. You can achieve your bulk copy in 3 steps:
Copy the files to tmp sub folder
Perform bulk rename, moving the files back into the current folder
Remove the tmp folder
I have a few files with the format ReportsBackup-20140309-04-00 and I would like to send the files with same pattern to the files as the example to the 201403 file.
I can already create the files based on the filename; I would just like to move the files based on the name to their correct folder.
I use this to create the directories
old="directory where are the files" &&
year_month=`ls ${old} | cut -c 15-20`&&
for i in ${year_month}; do
if [ ! -d ${old}/$i ]
then
mkdir ${old}/$i
fi
done
you can use find
find /path/to/files -name "*201403*" -exec mv {} /path/to/destination/ \;
Here’s how I’d do it. It’s a little verbose, but hopefully it’s clear what the program is doing:
#!/bin/bash
SRCDIR=~/tmp
DSTDIR=~/backups
for bkfile in $SRCDIR/ReportsBackup*; do
# Get just the filename, and read the year/month variable
filename=$(basename $bkfile)
yearmonth=${filename:14:6}
# Create the folder for storing this year/month combination. The '-p' flag
# means that:
# 1) We create $DSTDIR if it doesn't already exist (this flag actually
# creates all intermediate directories).
# 2) If the folder already exists, continue silently.
mkdir -p $DSTDIR/$yearmonth
# Then we move the report backup to the directory. The '.' at the end of the
# mv command means that we keep the original filename
mv $bkfile $DSTDIR/$yearmonth/.
done
A few changes I’ve made to your original script:
I’m not trying to parse the output of ls. This is generally not a good idea. Parsing ls will make it difficult to get the individual files, which you need for copying them to their new directory.
I’ve simplified your if ... mkdir line: the -p flag is useful for “create this folder if it doesn’t exist, or carry on”.
I’ve slightly changed the slicing command which gets the year/month string from the filename.
I am writing the following script to copy *.nzb files to a folder to queue them for Download.
I wrote the following script
#!/bin/bash
#This script copies NZB files from Downloads folder to HellaNZB queue folder.
${DOWN}="/home/user/Downloads/"
${QUEUE}="/home/user/.hellanzb/nzb/daemon.queue/"
for a in $(find ${DOWN} -name *.nzb)
do
cp ${a} ${QUEUE}
rm *.nzb
done
it gives me the following error saying:
HellaNZB.sh: line 5: =/home/user/Downloads/: No such file or directory
HellaNZB.sh: line 6: =/home/user/.hellanzb/nzb/daemon.queue/: No such file or directory
Thing is that those directories exsist, I do have right to access them.
Any help would be nice.
Please and thank you.
Variable names on the left side of an assignment should be bare.
foo="something"
echo "$foo"
Here are some more improvements to your script:
#!/bin/bash
#This script copies NZB files from Downloads folder to HellaNZB queue folder.
down="/home/myusuf3/Downloads/"
queue="/home/myusuf3/.hellanzb/nzb/daemon.queue/"
find "${down}" -name "*.nzb" | while read -r file
do
mv "${file}" "${queue}"
done
Using while instead of for and quoting variables that contain filenames protects against filenames that contain spaces from being interpreted as more than one filename. Removing the rm keeps it from repeatedly producing errors and failing to copy any but the first file. The file glob for -name needs to be quoted. Habitually using lowercase variable names reduces the chances of name collisions with shell variables.
If all your files are in one directory (and not in multiple subdirectories) your whole script could be reduced to the following, by the way:
mv /home/myusuf3/Downloads/*.nzb /home/myusuf3/.hellanzb/nzb/daemon.queue/
If you do have files in multiple subdirectories:
find /home/myusuf3/Downloads/ -name "*.nzb" -exec mv {} /home/myusuf3/.hellanzb/nzb/daemon.queue/ +
As you can see, there's no need for a loop.
The correct syntax is:
DOWN="/home/myusuf3/Downloads/"
QUEUE="/home/myusuf3/.hellanzb/nzb/daemon.queue/"
for a in $(find ${DOWN} -name *.nzb)
# escape the * or it will be expanded in the current directory
# let's just hope no file has blanks in its name
do
cp ${a} ${QUEUE} # ok, although I'd normally add a -p
rm *.nzb # again, this is expanded in the current directory
# when you fix that, it will remove ${a}s before they are copied
done
Why don't you just use rm $(a}?
Why use a combination of cp and rm anyway, instead of mv?
Do you realize all files will end up in the same directory, and files with the same name from different directories will overwrite each other?
What if the cp fails? You'll lose your file.