i can't get this program to work with files with spaces in them.
it keeps erroring out even when i modify it for the code to have "\ " before every space
what happened was i found this code online and it works as a shell script when you send the info and the files have no spaces, now i'm trying to get it to work in automator and with any file
iconSource="$1"
iconDestination="$2"
newiconSource="${iconSource}"
newiconDestination="${iconDestination}"
echo $newiconSource
echo $newiconDestination
icon=/tmp/`basename $newiconSource`
rsrc=/tmp/icon.rsrc
# Create icon from the iconSource
cp $newiconSource $icon
# Add icon to image file, meaning use itself as the icon
sips -i $icon
# Take that icon and put it into a rsrc file
DeRez -only icns $icon > $rsrc
# Apply the rsrc file to
SetFile -a C $newiconDestination
if [ -f $newiconDestination ]; then
# Destination is a file
Rez -append $rsrc -o $newiconDestination
elif [ -d $newiconDestination ]; then
# Destination is a directory
# Create the magical Icon\r file
touch $newiconDestination/$'Icon\r'
Rez -append $rsrc -o $newiconDestination/Icon?
SetFile -a V $newiconDestination/Icon?
fi
Here's a simpler piece of code reproducing your problem:
file="hello world"
rm $file
This outputs:
rm: cannot remove `hello': No such file or directory
rm: cannot remove `world': No such file or directory
(if it doesn't, it just deleted your files named "hello" and "world". Sorry about that.)
To fix it, you have to double quote all references to variables to prevent globbing and wordsplitting:
file="hello world"
rm "$file" # <- Note double quotes
ShellCheck automatically points this out. Run your script through it, and it'll point out most of the references that need quoting.
Related
I am trying to make a bash script to create directories with the same name as each file in a given directory, then move said files to their respective directories, and then rename the files.
Basically - a quantum chemistry program that I use requires that the input files be named "ZMAT". So, if I have multiple jobs, I currently need to manually create directories, and then move the ZMAT files into them (can only run one job per folder).
When I run my code, I get "binary operator expected". I am not sure what this means. Some help please.
Here is what I have so far:
#!/bin/bash
if [ -e *.ZMAT ];
then
echo "CFOUR Job Detected"
for INPFILE in *.ZMAT; do
BASENAME=$(basename $INPFILE )
INPFILE=$BASENAME.ZMAT
OUTFILE=$BASENAME.out
XYZFILE=$BASENAME.xyz
ERRORFILE=$BASENAME.slu
if [ ! -e $ERRORFILE ];
then
# Create folder in scratch directory with the basename
mkdir /scratch/CFOUR/$BASENAME
# Move the file to its directory
mv -f $INPFILE /scratch/CFOUR/$BASENAME
# cd to the new directory
cd /scratch/CFOUR/$BASENAME
# Change the file name to just ZMAT
mv -f $INPFILE ZMAT
echo "Submitting CFOUR Job"
# Submit to scheduler
#RUN_COMMAND="sbatch -J $BASENAME _CFOUR_MRCC_SLURM.SUB"
#eval $RUN_COMMAND
else
echo "Error File Detected - Not Submitting Job"
fi
done
fi
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
I believe I have solved my problem. Here is the new script, which appears to be working fine. Any input is welcome though!
#!/bin/bash
SUBDIR=$(pwd)
for i in *.ZMAT; do
BASENAME=$(basename $i .ZMAT)
INPFILE=$BASENAME.ZMAT
OUTFILE=$BASENAME.out
XYZFILE=$BASENAME.xyz
ERRORFILE=$BASENAME.slu
if [ ! -e $ERRORFILE ];
then
mkdir /scratch/CFOUR/$BASENAME # Create Scratch Folder
cp $INPFILE /scratch/cdc/CFOUR/$BASENAME # Move Input to Scratch
cd /scratch/CFOUR/$BASENAME #cd to Scratch Folder
mv -f $INPFILE ZMAT # Change Input Name
echo "Submitting CFOUR Job"
# Submit to scheduler
#RUN_COMMAND="sbatch -J $BASENAME _CFOUR_MRCC_SLURM.SUB"
#eval $RUN_COMMAND
cd $SUBDIR #Go back to SUBDIR
else
echo "Error File Already Exists"
fi
done
I want to create a script from terminal to be used like this one:
ddmg StartingDirectory
that will create into the StartingDirectory a dmg file for each subdirectory present.
Example:
\StartingDirectory
\MDT1D01
\MDT1D02
\MDT1D03
\MDT1DN
command should run for each subdirectory MDT1D0 (1..N)
and create a .dmg for each, with VolumeName and FileName as the name of the same subdirectory (that is MDT1D01 f.i.).
(VolumeName is the name that appears at the left side of the Finder when you open the dmg).
I already know that the command to create dmg is:
hdiutil create -volname VolumeName -srcfolder /path/to/the/folder/you/want/to/create -ov -format UDZO FileName.dmg
and this is working because I tested it.
I've already tried to create a personal command named dmg, in this way:
dmg(){
hdiutil create -volname “$1” -srcfolder “$2” -ov -format UDZO “$3.dmg”
}
that should be used in this way:
dmg VolumeName source/directory/path FileName
but it doesn't seem to work and I don't understand why.
In addition I found a template to create scripts (this is working but it says I need to install xtools to work properly, I suppose because of the git command that I don't need right now):
#!/bin/bash
#Use set -x if you want to echo each command while getting executed
#set -x
#Save current directory so we can restore it later
cur=$PWD
#Save command line arguments so functions can access it
args=("$#")
#Put your code in this function
#To access command line arguments use syntax ${args[1]} etc
function dir_command {
#This example command implements doing git status for folder
cd $1
echo "$(tput setaf 2)$1$(tput sgr 0)"
git tag -a ${args[0]} -m "${args[1]}"
git push --tags
cd ..
}
#This loop will go to each immediate child and execute dir_command
find . -maxdepth 1 -type d \( ! -name . \) | while read dir; do
dir_command "$dir/"
done
#This example loop only loops through give set of folders
declare -a dirs=("dir1" "dir2" "dir3")
for dir in "${dirs[#]}"; do
dir_command "$dir/"
done
#Restore the folder
cd "$cur"
With these info, could you help me to create the script I need?
I'm a newbie, so please be quite specific :)
Thanks a lot in advance!
I found a way to do that.
This is the code:
#!/bin/bash
#Use set -x if you want to echo each command while getting executed
#set -x
#Save current directory so we can restore it later
cur=$PWD
#Save command line arguments so functions can access it
args=("$#")
#Put your code in this function
#This loop will go to each immediate child and execute hdiutil
find . -depth 1 -type d | cut -c 3- | while read dir; do
hdiutil create -volname $dir -srcfolder $dir -format UDZO $dir.dmg
done
#Restore the folder
cd "$cur"
Note that I've cut the "-ov" option to hdiutil, so it's not going to overwrite an existing dmg.
It's very useful because if you have a lot of subdirectories, it could happen that you get errors at some point (for instance I got the "resource busy" error), and in this way you can simply relaunch the command without worrying about what directory has got the error.
Just tested and it's working flawlessly. 5 hours it's creating dmgs right now :)
I use a program to rip radio music. Sadly one can not set the temporary folder apart from the folder where the finished mp3's end up later. So I cannot set the output folder to auto add to iTunes.
I'm alright in coding java and what not but have no experience with shell scripts.
I need a script that iterates through all the files within a folder like every 10 minutes and moves them to a different location if they don't start with the string "Track". All temp files are called "Track..." so it should only move finished ones then. Could anyone give me a help getting started?
Thanks!
Here's an example script. You should set the DESTINATION directory properly before uncommenting the line which moves the files. Otherwise, you may end up moving them somewhere undesirable.
In the terminal, cd to the location where you save the snippet below and run the following commands to execute.
Prep work:
cd /save/location
chmod +x file_mover.sh # makes the file executable
Schedule a job:
crontab -e
*/10 * * * * /path/to/file_mover.sh
crontab -l # view list of scheduled jobs
With some minor tweaks you can make this accept CLI options.
#!/bin/bash
# files to skip
REGEX='^TRACK'
# location to move the files
DESTINATION=/tmp/mydir
# directory to read from
# PWD is the working directory
TARGET=${PWD}
# make the directory(ies) if it doesn't exists
if [ ! -f ${DESTINATION} ]; then
mkdir -p ${DESTINATION}
fi
# get the collection of files in the
for FILE in $( ls ${TARGET} )
do
# if the current file does not begin with TRACK, move it
if [[ ! ${FILE} =~ ${REGEX} ]]; then
echo ${FILE}
# SET THE DESTINATION DIRECTORY BEFORE UNCOMMENTING THE LINE BELOW
# if [ -f ${FILE} ]; then # uncomment if you want to
# ensure it's a file and not a directory
# mv ${FILE} ${DESTINATION} # move the file
# fi # uncomment to ensure it's a file (end if)
fi
done
Edit the crontab with EDITOR=nano crontab -e and add a line like this:
*/10 * * * * shopt -s extglob; mv ~/Music/Temp/!(Track)*.mp3 ~/Music/iTunes/iTunes\ Media/Automatically\ Add\ to\ iTunes.localized/
shopt -s extglob adds support for !(). See /usr/share/doc/bash/bash.html.
To set an icon on a file or directory is straight forward using the "Get Info" dialog in Finder.
copy image from e.g. Preview
open "Get Info" on file or directory
press TAB to select the icon
paste Cmd-V
But how do you do this using the command line?
Here is a bash script "setIcon.sh" for it
#!/bin/sh
# Sets an icon on file or directory
# Usage setIcon.sh iconimage.jpg /path/to/[file|folder]
iconSource=$1
iconDestination=$2
icon=/tmp/`basename $iconSource`
rsrc=/tmp/icon.rsrc
# Create icon from the iconSource
cp $iconSource $icon
# Add icon to image file, meaning use itself as the icon
sips -i $icon
# Take that icon and put it into a rsrc file
DeRez -only icns $icon > $rsrc
# Apply the rsrc file to
SetFile -a C $iconDestination
if [ -f $iconDestination ]; then
# Destination is a file
Rez -append $rsrc -o $iconDestination
elif [ -d $iconDestination ]; then
# Destination is a directory
# Create the magical Icon\r file
touch $iconDestination/$'Icon\r'
Rez -append $rsrc -o $iconDestination/Icon?
SetFile -a V $iconDestination/Icon?
fi
# Sometimes Finder needs to be reactivated
#osascript -e 'tell application "Finder" to quit'
#osascript -e 'delay 2'
#osascript -e 'tell application "Finder" to activate'
rm $rsrc $icon
Assuming that we have icns-file already. Create temp resource file which points to icns-file:
$ echo "read 'icns' (-16455) \"Icon.icns\";" >> Icon.rsrc
Append the resource file as value of extended attribute "com.apple.ResourceFork" to a file:
$ Rez -a Icon.rsrc -o FileName.ext
Show the icon of the file:
$ SetFile -a C FileName.ext
Append resource file as value of extended attribute "com.apple.ResourceFork" to a magic icon file inside current folder:
$ Rez -a Icon.rsrc -o Icon$'\r'
Show the icon of current folder:
$ SetFile -a C .
Hide the magic icon file inside current folder (press ⇧⌘. to show/hide hidden files in Finder):
$ SetFile -a V Icon$'\r'
Additional details
Icon data is stored as value of extended attribute "com.apple.ResourceFork" (Terminal command "xattr -p com.apple.ResourceFork FileName.ext" prints the value). For a folder there is magic (which is empty and hidden) file Icon$'\r' inside the folder. To extract icon data from extended attribute "com.apple.ResourceFork" into plain text resource file (from which we know correct icns-type identifier "-16455"):
$ DeRez -only icns FileWithIcon.ext > Icon.rsrc
$ DeRez -only icns /Folder/With/Icon/Icon$'\r' > Icon.rsrc
Under macOS 10.13 High Sierra command $ sips -i ImageFile.icns/png/jpg generates error --addIcon is no longer supported. Switch -i means "--addIcon" as extended attribute "com.apple.ResourceFork" onto this file itself using the content of this image file.
I'm trying to build a BASH script on OS X (10.6/10.7) to process a folder called QCExports which has folders with people's names in it in the format "LAST, First", i.e. "BOND, James".
When I run the following script, everything works but it falls over on folder or filenames with a space in them.
Script Code:
#!/bin/bash
echo "QuickCeph Export Script"
#Set Path to Process & Paths to Copy To
pathToQCExports=/Users/myuser/Desktop/QCExports
sureSmilePath=/Users/myuser/Desktop/QCExportsForSureSmile
sesamePath=/Users/myuser/Desktop/QCExportsForSesame
blankReplace=""
#Process Each Name
find $pathToQCExports -type d | while read name ; do
#escaping the folder with a space in the name
nameParsed=${name/", "/",\ "}
echo "Processing: "$nameParsed
pathSureSmile=${nameParsed/$pathToQCExports/$sureSmilePath}
pathSesame=${nameParsed/$pathToQCExports/$sesamePath}
mkdir $pathSesame
mkdir $pathSureSmile
echo "Folders Created"
#Copy For SureSmile
echo ${pathSureSmile}"/Suresmile-Frontal\ Photo.jpg" ${pathSureSmile}"/Suresmile-Frontal\ Photo.jpg"
#cp `${$pathSureSmile}"/Suresmile-Frontal\ Photo.jpg" ${pathSureSmile}"/Suresmile-Frontal\ Photo.jpg"`
#Copy For Sesame
echo ${pathSesame}"/Suresmile-Frontal\ Photo.jpg" ${pathSesame}"/S02.jpg"
#cp `${pathSesame}"/Suresmile-Frontal\ Photo.jpg" ${pathSesame}"/S02.jpg"`
done
echo "Completed";
Output:
QuickCeph Export Script
Processing: /Users/myuser/Desktop/QCExports/BOND,\ James
mkdir /Users/myuser/Desktop/QCExportsForSesame/BOND,\ James
mkdir: James: File exists
mkdir /Users/myuser/Desktop/QCExportsForSureSmile/BOND,\ James
mkdir: James: File exists
Folders Created
/Users/myuser/Desktop/QCExportsForSureSmile/BOND,\ James/Suresmile-Frontal\ Photo.jpg /Users/myuser/Desktop/QCExportsForSureSmile/BOND,\ James/Suresmile-Frontal\ Photo.jpg
/Users/myuser/Desktop/QCExportsForSesame/BOND,\ James/Suresmile-Frontal\ Photo.jpg /Users/myuser/Desktop/QCExportsForSesame/BOND,\ James/S02.jpg
Completed
On OS X usually in the terminal, you use a \ to escape a space in a folder or filename, but that doesn't seem to work.
I notice that it's interpreting the spaces as a normal space would be interpreted on the command line and thinking I want to execute the command on two files - i.e. it's not passing the \ onwards. I end up with a folder called "Bond,\" and a folder called "James" in the folder I execute the script from.
Note, I deliberately have the cp commands # out at the moment, so they aren't being executed... the problem is the same for both creating the folders & copying the filenames.
If I copy and paste the "echo'd" version of these commands into a terminal window, the commands work! But when BASH executes them, it doesn't respect the . :(
Any ideas?
Thanks!!
John
See my modifications on your script, you don't have to substitute spaces like you tried.
Moreover, you must choose if you backslash the spaces or if you are using quotes.
The simple way is to use double quotes.
Good doc about quotes, see http://mywiki.wooledge.org/Quotes and http://wiki.bash-hackers.org/syntax/words
#!/bin/bash
echo "QuickCeph Export Script"
#Set Path to Process & Paths to Copy To
pathToQCExports=/Users/myuser/Desktop/QCExports
sureSmilePath=/Users/myuser/Desktop/QCExportsForSureSmile
sesamePath=/Users/myuser/Desktop/QCExportsForSesame
blankReplace=""
#Process Each Name
find $pathToQCExports -type d | while read nameParsed ; do
echo "Processing: $nameParsed"
pathSureSmile="${nameParsed/$pathToQCExports/$sureSmilePath}"
pathSesame="${nameParsed/$pathToQCExports/$sesamePath}"
mkdir "$pathSesame"
mkdir "$pathSureSmile"
echo "Folders Created"
#Copy For SureSmile
echo "${pathSureSmile}/Suresmile-Frontal Photo.jpg" "${pathSureSmile}/Suresmile-Frontal Photo.jpg"
#Copy For Sesame
echo "${pathSesame}/Suresmile-Frontal Photo.jpg" "${pathSesame}/S02.jpg"
done
echo "Completed"