<?xml version="1.0" encoding="utf-8"?>
<rss version="2.0"
	xmlns:content="http://purl.org/rss/1.0/modules/content/"
	xmlns:wfw="http://wellformedweb.org/CommentAPI/"
	xmlns:dc="http://purl.org/dc/elements/1.1/"
	xmlns:atom="http://www.w3.org/2005/Atom"
	xmlns:sy="http://purl.org/rss/1.0/modules/syndication/"
	xmlns:slash="http://purl.org/rss/1.0/modules/slash/"
	>

<channel>
	<title>Everything In Between &#187; Bash/Shell Scripting</title>
	<atom:link href="http://maymay.net/blog/category/programming/bashshell-scripting/feed/" rel="self" type="application/rss+xml" />
	<link>http://maymay.net/blog</link>
	<description>The brutally honest, first-person account of Meitar Moscovitz&#039;s life.</description>
	<lastBuildDate>Thu, 19 Jan 2012 08:54:24 +0000</lastBuildDate>
	<language>en</language>
	<sy:updatePeriod>hourly</sy:updatePeriod>
	<sy:updateFrequency>1</sy:updateFrequency>
	<generator>http://wordpress.org/?v=3.3.1</generator>
		<item>
		<title>How to work around &#8220;sorry, you must have a tty to run sudo&#8221; without sacrificing security</title>
		<link>http://maymay.net/blog/2010/03/17/how-to-work-around-sorry-you-must-have-a-tty-to-run-sudo-without-sacrificing-security/</link>
		<comments>http://maymay.net/blog/2010/03/17/how-to-work-around-sorry-you-must-have-a-tty-to-run-sudo-without-sacrificing-security/#comments</comments>
		<pubDate>Thu, 18 Mar 2010 01:21:50 +0000</pubDate>
		<dc:creator>Meitar</dc:creator>
				<category><![CDATA[Bash/Shell Scripting]]></category>
		<category><![CDATA[Security & Privacy]]></category>
		<category><![CDATA[Tech/Computing]]></category>
		<category><![CDATA[Unix/Linux]]></category>

		<guid isPermaLink="false">http://maymay.net/blog/?p=1208</guid>
		<description><![CDATA[While working on $client&#8216;s Linux server last week, I found myself installing a cron job that ran as root. The cron job called a custom bash script that, in turn, called out to various custom maintenance tasks client had already written. One task in particular had to run as a different user. During testing, I [...]]]></description>
			<content:encoded><![CDATA[<p>While working on $<var>client</var>&#8216;s Linux server last week, I found myself installing a cron job that ran as <code>root</code>. The cron job called a custom bash script that, in turn, called out to various custom maintenance tasks <var>client</var> had already written. One task in particular had to run as a different user.</p>
<p>During testing, I discovered that the odd-ball task failed to run, and found the following error in the system log:</p>
<pre>sudo: sorry, you must have a tty to run sudo</pre>
<p>I traced this error to a line trying to invoke a <code>perl</code> command as a user called <code>dynamic</code>:</p>
<pre>sudo -u dynamic /usr/bin/perl run-periodic-tasks --load 5 --randomly</pre>
<p>A simple Google search turned up an obvious solution to the error: <a href="http://www.adminmyserver.com/articles/sorry-you-must-have-a-tty-to-run-sudo">use <code>visudo</code> to disable sudo&#8217;s tty requirement</a>, allowing <code>sudo</code> to be invoked from any shell lacking a tty (including <code>cron</code>). This would have solved my problem, but it just felt wrong, dirty, and most troublingly <em>insecure</em>.</p>
<p>One reason why <code>sudo</code> ships with the <code>requiretty</code> option enabled by default is, among other reasons, to <a href="http://www.cyberciti.biz/faq/linux-unix-bsd-sudo-sorry-you-must-haveattytorun/">prevent remote users from exposing the root password over <acronym title="Secure SHell">SSH</acronym></a>. Disabling this security precaution for a simple maintenance task <em>already running as root</em> seemed totally unnecessary, not to mention irresponsible. Moreover, <var>client</var>&#8216;s script didn&#8217;t even need a tty.</p>
<p>Thankfully, there&#8217;s a better way: use <code>su --session-command</code> and send the whole job to the background.</p>
<pre>su --session-command="/usr/bin/perl run-periodic-tasks --load 5 --randomly" dynamic &#038;</pre>
<p>This line launches a new, non-login shell (typically <code>bash</code>) <em>as the other user</em> in a separate, background process and runs the command you passed using the shell&#8217;s <code>-c</code> option. Sending the command to the background (using <code>&#038;</code>) continues execution of the rest of the cron job.</p>
<p>A process listing would look like this:</p>
<pre>root     28109     1  0 17:10 ?        00:00:00 su --session-command=/usr/bin/perl run-periodic-tasks --load 5 --randomly dynamic
dynamic  28110 28109  0 17:10 ?        00:00:00 bash -c /usr/bin/perl run-periodic-tasks --load 5 --randomly</pre>
<p>Note the parent process (PID 28109) is owned by root but the actual <code>perl</code> process (PID 28110) is being run as <code>dynamic</code>.</p>
<p>This in-script solution that replaces <code>sudo -u <var>user</var> <var>cmd</var></code> with <code>su --session-command=<var>cmd</var> <var>user</var></code> seems much better than relying on a change in <code>sudo</code>&#8216;s default (and more secure) configuration to me.</p>
]]></content:encoded>
			<wfw:commentRss>http://maymay.net/blog/2010/03/17/how-to-work-around-sorry-you-must-have-a-tty-to-run-sudo-without-sacrificing-security/feed/</wfw:commentRss>
		<slash:comments>6</slash:comments>
		</item>
		<item>
		<title>Extract list of all Apple WikiServer wiki titles into CSV format</title>
		<link>http://maymay.net/blog/2008/09/22/extract-list-of-all-apple-wikiserver-wiki-titles-into-csv-format/</link>
		<comments>http://maymay.net/blog/2008/09/22/extract-list-of-all-apple-wikiserver-wiki-titles-into-csv-format/#comments</comments>
		<pubDate>Mon, 22 Sep 2008 05:35:55 +0000</pubDate>
		<dc:creator>Meitar</dc:creator>
				<category><![CDATA[Bash/Shell Scripting]]></category>
		<category><![CDATA[Crosspost]]></category>
		<category><![CDATA[Mac OS X]]></category>
		<category><![CDATA[Tech/Computing]]></category>

		<guid isPermaLink="false">http://maymay.net/blog/?p=652</guid>
		<description><![CDATA[An interesting request came in today from a coworker. She wanted to create a spreadsheet that contained all of our intranet&#8217;s wiki pages (which uses the Apple WikiServer), presumably because Apple doesn&#8217;t provide an easy way to &#8220;list all pages&#8221; in the wiki itself. Along with the page title, she also wanted to extract its [...]]]></description>
			<content:encoded><![CDATA[<p>An interesting request came in today from a coworker. She wanted to create a spreadsheet that contained all of our intranet&#8217;s wiki pages (which uses the Apple WikiServer), presumably because Apple doesn&#8217;t provide an easy way to &#8220;list all pages&#8221; in the wiki itself. Along with the page title, she also wanted to extract its internal ID, its <acronym title="Uniform Resource Locator">URL</acronym>, and the time the page was created as well as the time it was last modified.</p>
<p>I spent about an hour looking into this this afternoon and it turns out that much of this information is readily available on the filesystem in the Apple WikiServer&#8217;s data store. I whipped up the following shell script to extract this information in CSV format, exactly as requested.</p>
<p>I&#8217;m posting this script here in case someone else wants similar &#8220;export a list of WikiServer pages to a comma-separated values (CSV) file&#8221; functionality but isn&#8217;t sure how to go about getting it. To use this, just edit the line that reads <code>http://my-server.example.com/groups/wiki/</code> so that it refers to the wiki base <acronym title="Uniform Resource Identifier">URI</acronym> of your own server.</p>
<p><ins datetime="2008-09-23T07:15:49+00:00"><strong>Update:</strong> The latest version of this script is now available at <a href="//github.com/meitar/wikipages2csv/">its Github-hosted repository</a>. <strong>You should probably use that instead of the script below.</strong></ins></p>
<pre class="shell">
#!/bin/<var>sh</var> -
#
# Script to extract data from an Apple WikiServer's data store by querying the
# filesystem itself. Creates a 'wikipages.csv' file that's readable by any
# spreadsheeting application, such as Numbers.app or Microsoft Excel.app.
#
# USAGE:   To use this script, change to the WikiServer's pages directory, then
#          just run this script. A file named wikipages.csv will be created in
#          your current directory. For instance:
#
#              cd /Library/Collaboration/Groups/mygroup/wiki  # dir to work in
#              wikipages2csv.sh                               # run the script
#              cp wikipages.csv ~/Desktop                     # save output
#
# WARNING: Since the WikiServer's files are only accessible as root, this script
#          must be run as root to function. Additionally, this is not extremely
#          well tested, so use at your own risk.
#
# Author:  Meitar Moscovitz
# Date:    Mon Sep 22 15:03:54 EST 2008

##### CONFIGURE HERE ########

# The prefix to append to generated links. NO SPACES!
WS_URI_PREFIX=http://my-server.example.com/groups/wiki/

##### END CONFIGURATION #####
# DO NOT EDIT PAST THIS LINE
#############################

WS_CSV_OUTFILE=wikipages.csv
WS_PAGE_IDS_FILE=`mktemp ws-ids.tmp.XXXXXX`

function extractPlistValueByKey () {
    head -n \
      $(expr 1 + `grep -n "&lt;key&gt;$1&lt;/key&gt;" page.plist | cut -d ':' -f 1`) page.plist | \
        tail -n 1 | cut -d '&gt;' -f 2 | cut -d '&lt;' -f 1
}

function linkifyWikiServerTitle () {
    echo $1 | sed -e 's/ /_/g' -e 's/&amp;amp;/_/g' -e 's/&amp;gt;/_/g' -e 's/&amp;lt;/_/g' -e 's/\?//g'
}

function formatISO8601date () {
    echo $1 | sed -e 's/T/ /' -e 's/Z$//'
}

function csvQuote () {
    echo $1 | grep -q ',' &gt;/dev/null
    if [ $? -eq 0 ]; then
        echo '"'$1'"'
    else
        echo $1
    fi
}

ls -d [^w]*.page | \
  sed -e 's/^\([a-f0-9][a-f0-9][a-f0-9][a-f0-9][a-f0-9]\)\.page$/\1/' &gt; $WS_PAGE_IDS_FILE

echo "Title,ID,Date Created,Last Modified,<acronym title="Uniform Resource Identifier">URI</acronym>" &gt; $WS_CSV_OUTFILE
while read id; do
    cd $id.page
    title=$(extractPlistValueByKey title)
    created_date="$(formatISO8601date $(extractPlistValueByKey createdDate))"
    modified_date="$(formatISO8601date $(extractPlistValueByKey modifiedDate))"
    link=$WS_URI_PREFIX"$id"/`linkifyWikiServerTitle "$title"`.html
    cd ..
    echo `csvQuote "$title"`,$id,$created_date,$modified_date,`csvQuote "$link"` &gt;&gt; $WS_CSV_OUTFILE
done &lt; $WS_PAGE_IDS_FILE
rm $WS_PAGE_IDS_FILE
</pre>
<p>For those new to the Wiki Server, this <a href="/blog/2008/04/05/a-web-developers-introduction-to-the-apple-wikiserver-part-1/">introduction to the Apple WikiServer for web developers</a> may be of interest.</p>
]]></content:encoded>
			<wfw:commentRss>http://maymay.net/blog/2008/09/22/extract-list-of-all-apple-wikiserver-wiki-titles-into-csv-format/feed/</wfw:commentRss>
		<slash:comments>1</slash:comments>
		</item>
		<item>
		<title>One Minute Mac Tip: Remove .DS_Store files from ZIP Archives</title>
		<link>http://maymay.net/blog/2008/08/04/one-minute-mac-tip-remove-ds_store-files-from-zip-archives/</link>
		<comments>http://maymay.net/blog/2008/08/04/one-minute-mac-tip-remove-ds_store-files-from-zip-archives/#comments</comments>
		<pubDate>Mon, 04 Aug 2008 06:07:51 +0000</pubDate>
		<dc:creator>Meitar</dc:creator>
				<category><![CDATA[Bash/Shell Scripting]]></category>
		<category><![CDATA[Crosspost]]></category>
		<category><![CDATA[Mac OS X]]></category>
		<category><![CDATA[Productivity]]></category>
		<category><![CDATA[Programming]]></category>
		<category><![CDATA[Tech/Computing]]></category>
		<category><![CDATA[Windows]]></category>

		<guid isPermaLink="false">http://maymay.net/blog/?p=544</guid>
		<description><![CDATA[The Mac OS X Finder has some nifty features, one of which is an exceptionally useful contextual menu item to create ZIP archives of folders. Unfortunately, the Finder also has some really, really annoying habits, one of which is to create a file named .DS_Store in each folder a user opens (when not in Column [...]]]></description>
			<content:encoded><![CDATA[<p>The Mac <acronym title="Operating System">OS</acronym> X Finder has some nifty features, one of which is an exceptionally useful contextual menu item to create ZIP archives of folders. Unfortunately, the Finder also has some really, really annoying habits, one of which is to create a file named <code>.DS_Store</code> in each folder a user opens (when not in Column view). What this means is that if you create a ZIP archive on your Mac and then send it to someone who unzips it without the Finder (such as a Windows user using the Windows Explorer), the recipient will see a lot of litter in the form of useless and meaningless <code>.DS_Store</code> files.</p>
<p>If you&#8217;re not afraid of the Terminal, this can be avoided. Put the following lines in your <code>~/.profile</code> (or similar):</p>
<pre class="shell">
alias rmds='find . -name ".DS_Store" -type f -print0 | xargs -0 rm'
</pre>
<p>What this does is creates a new command that you can use (<code>rmds</code>) which recursively finds and deletes any regular file named &#8220;.DS_Store&#8221; starting from the current directory. Thus, running this command in the folder you are about to create an archive out of will clean it first, and will prevent unnecessary confusion on the part of your archive file recipient.</p>
<p>Alternatively, another way to do this is to use the command-line <code>zip</code> program and an (admittedly more complicated) pipeline to remove the <code>.DS_Store</code> files <em>after</em> they have been added to the archive. To do that, use this series of commands:</p>
<pre class="shell">
zip -d <var>ZIPfile.zip</var> `unzip -l <var>ZIPfile.zip</var> | grep .DS_Store | awk '{print $4}'`
</pre>
<p>where, naturally, <var>ZIPfile.zip</var> is the ZIP archive you want to remove the <code>.DS_Store</code> files from. Creating an alias out of that command (and making it work for paths that contain spaces) is left as an exercise for the reader. ;)</p>
<p>As an aside, the <code>alias</code>, <code>find</code> and <code>xargs</code> commands are incredibly useful in their own right and can be used to do a lot of pretty amazing things. As always, <code>man <var>command</var></code> will give you the nitty gritty.</p>
<p>Also as an aside, you can stop the Finder from creating <code>.DS_Store</code> files entirely when browsing network volumes (like Windows shares) with another command, <a href="//support.apple.com/kb/HT1629">documented in Apple&#8217;s Knowledge Base</a>.</p>
]]></content:encoded>
			<wfw:commentRss>http://maymay.net/blog/2008/08/04/one-minute-mac-tip-remove-ds_store-files-from-zip-archives/feed/</wfw:commentRss>
		<slash:comments>5</slash:comments>
		</item>
		<item>
		<title>Quick &#8216;N&#8217; Dirty Drupal Module SVN Tagging Script</title>
		<link>http://maymay.net/blog/2008/05/14/quick-n-dirty-drupal-module-svn-tagging-script/</link>
		<comments>http://maymay.net/blog/2008/05/14/quick-n-dirty-drupal-module-svn-tagging-script/#comments</comments>
		<pubDate>Wed, 14 May 2008 09:46:14 +0000</pubDate>
		<dc:creator>Meitar</dc:creator>
				<category><![CDATA[Bash/Shell Scripting]]></category>
		<category><![CDATA[Productivity]]></category>
		<category><![CDATA[Tech/Computing]]></category>
		<category><![CDATA[Unix/Linux]]></category>
		<category><![CDATA[Drupal]]></category>
		<category><![CDATA[Subversion]]></category>

		<guid isPermaLink="false">http://maymay.net/blog/?p=435</guid>
		<description><![CDATA[In a (rather beastly) project at work today, I found myself needing to import a significant number of contributed Drupal modules into Subversion vendor branches to prepare for custom development. To do so manually would have been quite the hassle, so after downloading the appropriate tarballs and creating a module_name/current directory under my vendor/drupal/modules vendor [...]]]></description>
			<content:encoded><![CDATA[<p>In a (rather beastly) project at work today, I found myself needing to import a significant number of contributed Drupal modules into Subversion vendor branches to prepare for custom development. To do so manually would have been quite the hassle, so after downloading the appropriate tarballs and creating a <code><var>module_name</var>/current</code> directory under my <code>vendor/drupal/modules</code> vendor branch directory, I concocted this little (relatively untested) script to handle the mass tagging operations I needed to perform.</p>
<pre>for i in *; do
    v=`grep 'version = "' "$i/current/$i/"*.info |
      cut -d ':' -f 2 |
        sed -e 's/^version = "/v/' -e 's/"$//'`
    svn cp "$i/current" "$i/$v"
done;</pre>
<p>It&#8217;s a bit buggy for some modules that have multiple <code>.info</code> files, but I&#8217;m sure a few more pipeline stages can fix that. (Which, because I&#8217;m done with this at the moment, I will leave as an exercise to the reader.)</p>
<p>Chalk this one up as another testament to the power of shell scripting and how it can help every developer get their job done faster.</p>
]]></content:encoded>
			<wfw:commentRss>http://maymay.net/blog/2008/05/14/quick-n-dirty-drupal-module-svn-tagging-script/feed/</wfw:commentRss>
		<slash:comments>1</slash:comments>
		</item>
	</channel>
</rss>

