Posted mostly for my own reference since I always forget the syntax, arrays are invaluable when writing moderately complex shell scripts. If you're writing any serious shell scripts you'll want to refer The Linux Documentation Project's excellent primers: Bash Guide for Beginners and Advanced Bash-Scripting Guide
array.sh
#!/bin/bash
echo "<<< Load a file into an array"
echo "# Set the IFS (Internal Field Separator) to a newline
IFS='
'
# Load file.txt from the current directory
arr=( \$( < file.txt ) )
"
IFS='
'
arr=( $( < file.txt ) )
echo "<<< Addressing individual array elements"
echo "\${arr[0]} = ${arr[0]}" # the first line of the file
echo "\${arr[1]} = ${arr[1]}" # the second line of the file
echo ""
echo "<<< \${#VARNAME[@]} will always return the number of elements in an array"
echo "\$arr contains ${#arr[@]} (\${#arr[@]}) items"
echo ""
echo "<<< Loop through the array (\${arr[@]}), loading each item as \$foo."
num=1
for foo in "${arr[@]}" ; do
echo "Loop iteration $num: $foo"
num=$((num+1))
done
echo ""
echo "<<< Loop through the array, addressing each item with an index"
num=0
while [[ $num -lt ${#arr[@]} ]] ; do
echo "Array index $num (\${arr[$num]}): ${arr[$num]}"
num=$((num+1))
done
file.txt
file line 1
file line 2
file line 3
file line 4
Saving the two files above as array.sh and file.txt, and running array.sh yields:
$./array.sh
<<< Load a file into an array
# Set the IFS (Internal Field Separator) to a newline
IFS='
'
# Load file.txt from the current directory
arr=( $( < file.txt ) )
<<< Addressing individual array elements
${arr[0]} = file line 1
${arr[1]} = file line 2
<<< ${#VARNAME[@]} will always return the number of elements in an array
$arr contains 4 (${#arr[@]}) items
<<< Loop through the array (${arr[@]}), loading each item as $foo.
Loop iteration 1: file line 1
Loop iteration 2: file line 2
Loop iteration 3: file line 3
Loop iteration 4: file line 4
<<< Loop through the array, addressing each item with an index
Array index 0 (${arr[0]}): file line 1
Array index 1 (${arr[1]}): file line 2
Array index 2 (${arr[2]}): file line 3
Array index 3 (${arr[3]}): file line 4
I've had an issue on a server for a little while now where Apache works fine, but apachectl configtest throws a warning.
# apachectl configtest
httpd: apr_sockaddr_info_get() failed for foo.example.com+
httpd: Could not reliably determine the server's fully qualified domain name, using 127.0.0.1 for ServerName
Everything worked, so I never dug too deep, but each time I saw the message I got a little more annoyed. I thought there might be a hidden character in my httpd.conf file, but the actual issue wasn't related to Apache at all – it was the machine's hostname. I never noticed it because with a machine named foo.example.com I'd only see username@foo at a stock bash command prompt.
Running hostname showed that the the hostname had a trailing +
# hostname
foo.example.com+
It was easily fixed by running:
# hostname foo.example.com
I'm working on an Folder Action to automatically create some symlinks, and needed to account for a finite number of temporary files, which requires that I know if a symlink is valid or not.
Apple's Developer Tools ships with a utility called GetFileInfo that does just the trick.
/Developer/Tools/GetFileInfo -aa _SYMLINK_ >/dev/null 2>&1 ; echo $?
This will return 0 if the symlink (_SYMLINK_) points to a valid file, and as near as I can tell, 3 if the file does not exist.
Bonus
On Linux you can find the filenames of all broken symlinks in a given directory using find/xargs/grep/sed with
find . -type l -print0 | xargs -0 file | grep "broken symbolic" | sed -e 's/^\|: *broken symbolic.*$/"/g'
The only piece that doesn't work on OS X is the sed regexp (which I'm too lazy to fix), but you can kind of work around that with cut, as long as your symlink filenames don't contain a colon.
find . -type l -print0 | xargs -0 file | grep "broken symbolic" | cut -d':' -f1
I recently needed to be able to mount volumes on a Linux server from a script and I ran into the "mount: only root can do that" error, even with an intermediate SUID script (because I believe mount checks the real user id, not just the effective one). The device and mount point are not consistent so adding an entry to /etc/fstab wasn't an option, nor was passing a password to sudo.
Enter the /etc/sudoers file. By default sudo requires that a user provide their password, but you can use the NOPASSWD option to bypass this requirement. This was perfect.
To allow the user corey to run /bin/mount and /bin/umount on all machines without a password add the following line to /etc/sudoers:
corey ALL=NOPASSWD: /bin/mount, /bin/umount
To allow all members of the group 'wheel' to run /bin/mount and /bin/umount on all machines without a password add the following line to /etc/sudoers:
%wheel ALL=NOPASSWD: /bin/mount, /bin/umount
Now one of the privileged users can run:
sudo mount /dev/sdb1 /some/path/to/mountpoint
And mount without issue – or a password prompt.