Automator passing different variables to a shell script - shell

I try to use automator for renaming multiple files
I got this far:
the first variable must be inside the Exiftool command line
in this case I selected 2 files, but that could be 1 or 100 files
how do I make this happen? is it possible to start from array key 1 instead of array key 0 for the filenames?

The standard way to do this is to store $1 in a variable, then use shift to remove it from the argument list, and then use "$#" to get all of the remaining arguments (i.e. the original "$2" "$3" "$4" ...) Something like this:
RenameTo="$1"
shift
echo "New name: $RenameTo"
echo "files:" "$#"
I'm not sure exactly what you're trying to to with exiftool, so I won't try to give that full command.
Note that the double-quotes aren't required in zsh, but they make this portable to POSIX-compliant shells. Also, echo isn't a very good way to see what a command would do, because it looses the distinction between spaces within an argument (e.g. spaces in the new name, or within a filename) and spaces between arguments (e.g. between the filenamess in a list of them).

Related

Setting specific variables from a different script

I need to take specific variables from one script and use them in a different script.
Example:
Original script:
VARA=4 # Some description of VARA
VARB=6 # Some description of VARB
SOMEOTHERVAR="Foo"
/call/to/some/program
I want to write a second script that needs VARA and VARB, but not SOMEOTHERVAR or the call to the program.
I can already do:
eval $(grep 'VARA=' origscript.sh)
eval $(grep 'VARB=' origscript.sh)
This seems to work, but when I want to do both, like this, it only sets the first:
eval $(grep 'VAR[AB]=' origscript.sh)
because it seems to concatenate the two lines that grep returns. (Which probably means that the comments save the first assignments.)
Put quotes around it, so that the newlines in the output of grep will not be turned into spaces.
eval "$(grep 'VAR[AB]=' origscript.sh)"

Bash for loop testing two boolean expressions

Below is a simple bash program. It takes file types as command line arguments and it queries the current directory and prints the files of the type specified.
I would like to be able to query two different file types and therefore need two boolean expressions to represent this.
Below is my code for querying just one file type
#!/bin/bash
for x in $(ls *$1); do
echo $x;
done
Now what I would like to be able to do is (in pseudocode)
command line args fileName .sh .c
for x in (current directory files of *.sh) OR (in current directory files of *.c) do
print .sh files
print.c files
done
I've tried using || and I get syntax errors I can not find any evidence of being able to use || for two expressions in for loop.
I've tried using two nested for loops but they do not work and yield errors.
Is there any way I can accomplish this using the same for loop system.
Thank you.
Sounds like you want something like:
for extension in "$#"; do
printf 'Files ending in %s:\n' "$extension"
printf '%s\n' *"$extension"
done
Loop through all arguments passed to the script and print all files ending in each extension + a newline character.
Note that printf is a much more useful tool than echo, as it allows you to control the format of each thing is prints.
ls doesn't do anything useful either here; it is the shell which expands the * to the list of files matching the pattern.

creating multiple directories based on value of variable

I have variable "one" which contains following
avi,mkw,dvd,cd
im trying to dynamicly create directories that would look like this
type-avi
type-mkw
type-dvd
type-cd
I have tried to achieve wanted result with following code
mkdir type-{"$one"}
but instead of creating 4 directories , it created one directory called
type-{avi,mkw,dvd,cd}
I suppose this is wrong method.. if so , how can i create dynamicly directories with "suffixes" stored in variabe?
Use an array instead of your string variable for this.
IFS=, read -a onearr <<<"$one"
mkdir "${onearr[#]/#/type-}"
Or if you don't need the $one string in the first place just create the array manually.
onearr=(avi mkw dvd cd)
mkdir "${onearr[#]/#/type-}"
If you aren't worried about spaces or anything in the values in $one and can trust your input to be "safe" and not exploitative and can't use read then you could use this to create the array instead (but it is just flat out a worse soluton).
onearr=($(tr , ' ' <<<"$one"))
A way to do this without reading into the shell, in a traditional tools pipeline approach:
echo "$one" |
tr ',' '\n' |
sed "s/^/mkdir 'type-/; s/$/'/" |
sh -x
Your original attempt was very close. To make it work, you can use the shell eval command:
eval mkdir type-{$one}
or
echo mkdir type{"$one"} | bash
In either case, the effect causes bash to re-evaluate the line.
I personally would not recommend this approach for these reasons:
eval can be a security risk and is little used, maintainers will have to do a double-take.
Brace Expansion is a bash-type shell extension and while I love bash, I write all shell scripts to run with the POSIX /bin/sh.
These will not handle unusual characters in filenames, such as spaces.
The eval causes the shell to re-evaluate the string after the variable substition has been performed. To gain more understanding on these topics, see "Brace Expansion" and also the eval command, both on the bash man page.

Pass more than 10 arguments to Sox shell script in automator

I am passing arguments (selected finder items) in Automator, to a shell script (Sox script to convert wav files to u-law wav files). I have two problems:
If there are more than 10 items selected, the shell script ignores anything past the first 10 arguments, and also, as the script stands right now, even when passing lessing than 10 arguments (finder items) the shell script will act on all but the last selected item. So, if I select 3 files in automator, the first 2 will get through, not the third. Or if I select 4 files, 3 files will make it through - and so on.
Here is my Automator Action order
Ask for Finder items
Set Value of Variable
labelled variable "input-files"
Get Value of Variable
get variable "input-files"
Run Shell Script
#! /bin/sh
soxloc="/usr/local/bin/sox";
tempfile="";
shopt -s nullglob
for f in "${#:1}"/*.wav
do
"$soxloc" "$f" -r 8000 -c 1 -e u-law "${f%.*}"-ulaw.wav
done
There are a few solutions listed on SO including the below link, but I'm just not sure how to integrate any of these solutions into my code:
How to handle more than 10 parameters in shell
Any help would be greatly appreciated!
The expression "${#:1}" expands to the tokens that were passed in as arguments, and then you add /*.wav at the end, resulting in a paste of the last token with the wildcard. With nullglob, this pattern will be replaced with nothing if there are no matches.
(The :1 nominally selects arguments starting from the first, but that's the default anyway, so that's superfluous here; the expression is equivalent to the simpler "$#" at least in my Bash.)
You probably intend something like
for d in "$#"; do
for f in "$d"/*.wav; do
sox "$f" -r 8000 -c 1 -e u-law "${f%.*}"-ulaw.wav
done
done
Putting sox in a variable is an antipattern. Add the (directory part of the) location to your PATH before the loop if it is not in a standard location.
This fixes the errors in the shell script so that it reliably accepts an arbitrary number of arguments; but if Automator has a limit of its own, I don't know how to fix that.

Iterate over all files in a directory, or only specified files in sh

I have a directory config with the following file listing:
$ ls config
file one
file two
file three
I want a bash script that will, when given no arguments, iterate over all those files; when given names of files as arguments, I want it to iterate over the named files.
#!/bin/sh
for file in ${#:-config/*}
do
echo "Processing '$file'"
done
As above, with no quotes around the list term in the for loop, it produces the expected output in the no-argument case, but breaks when you pass an argument (it splits the file names on spaces.) Quoting the list term (for file in "${#:-config/*}") works when I pass file names, but fails to expand the glob if I don't.
Is there a way to get both cases to work?
For a simpler solution, just modify your IFS variable
#!/bin/bash
IFS=''
for file in ${#:-config/*}
do
echo "Processing '$file'"
done
IFS=$' \n\t'
The $IFS is a default shell variable that lists all the separators used by the shell. If you remove the space from this list, the shell won't split on space anymore. You should set it back to its default value after you function so that it doesn't cause other functions to misbehave later in your script
NOTE: This seems to misbehave with dash (I used a debian, and #!/bin/sh links to dash). If you use an empty $IFS, args passed will be returned as only 1 file. However, if you put some random value (i.e. IFS=':'), the behaviour will be the one you wanted (except if there is a : in your files name)
This works fine with #!/bin/bash, though
Set the positional parameters explicitly if none are given; then the for loop is the same for both cases:
[ $# -eq 0 ] && set -- config/*
for file in "$#"; do
echo "Processing '$file'"
done
Put the processing code in a function, and then use different loops to call it:
if [ $# -eq 0 ]
then for file in config/*
do processing_func "$file"
done
else for file in "$#"
do processing_func "$file"
done
fi

Resources