{"id":7453,"date":"2013-03-28T18:42:42","date_gmt":"2013-03-28T17:42:42","guid":{"rendered":"https:\/\/blog.trifork.com\/?p=7453"},"modified":"2013-03-28T18:42:42","modified_gmt":"2013-03-28T17:42:42","slug":"bash-a-few-commands-to-use-again-and-again","status":"publish","type":"post","link":"https:\/\/trifork.nl\/blog\/bash-a-few-commands-to-use-again-and-again\/","title":{"rendered":"Bash &#8211; A few commands to use again and again"},"content":{"rendered":"<p><b>Introduction<\/b><\/p>\n<p>These days I spend a lot of time in the bash shell. I use it for ad-hoc scripting or driving several Linux boxes. In my current project we set up a <a href=\"http:\/\/en.wikipedia.org\/wiki\/Continuous_delivery\">continuous delivery<\/a> environment and migrate code onto it. I lift code from CVS to SVN, mavenize Ant builds and funnel artifacts into Nexus. One script I wrote determines if a jar that was checked into a CVS source tree exists in Nexus or not. This check can be done via the <a href=\"http:\/\/www.sonatype.com\/people\/2012\/07\/learning-the-nexus-rest-api-read-the-docs-or-fire-up-a-browser\/\">Nexus REST API<\/a>. More on this script at the end of the blog. But first let&#8217;s have a look at a few bash commands that I use all the time in day-to-day bash usage, in no particular order.<\/p>\n<ol>\n<li><b>find<\/b><\/li>\n<p>Find searches files recursively in the current directory.<\/p>\n<p><code>$ find -name *.jar<\/code><\/p>\n<p>This command lists all jars in the current directory, recursively. We use this command to figure out if a source tree has jars. If this is the case we add them to Nexus and to the pom as part of the migration from Ant to Maven.<\/p>\n<p><code>$ find -name *.jar -exec sha1sum {} \\;<\/code><\/p>\n<p>Find combined with exec is very powerful. This command lists the jars and computes sha1sum for each of them. The shasum command is put directly after the -exec flag. The {} will be replaced with the jar that is found. The \\; is an escaped semicolon for find to figure out when the command ends.<\/p>\n<li><b>for<\/b><\/li>\n<p>For loops are often the basis of my shell scripts. I start with a for loop that just echoes some values to the terminal so I can check if it works and then go from there.<\/p>\n<p><code><br \/>\n$ for i in $(cat items.txt); do echo $i; done;<br \/>\n<\/code><\/p>\n<p>The for loop keywords should be followed by either a newline or an &#8216;;&#8217;. When the for loop is OK I will add more commands between the do and done blocks. Note that I could have also used find -exec but if I have a script that is more than a one-liner I prefer a for loop for readability.<\/p>\n<li><b>tr<\/b><\/li>\n<p>Transliterate. You can use this to get rid of certain characters or replace them, piecewise.<\/p>\n<p>$ echo &#8216;Com_Acme_Library&#8217; | tr &#8216;_A-Z&#8217; &#8216;.a-z&#8217;<\/p>\n<p>Lowercases and replaces underscores with dots.<\/p>\n<li><b>awk<\/b><\/li>\n<p><code><br \/>\n$ echo 'one two three' | awk '{ print $2, $3 }'<br \/>\n<\/code><\/p>\n<p>Prints the second and third column of the output. Awk is of course a full blown programming language but I tend to use this snippets like this a lot for selecting columns from the output of another command.<\/p>\n<li><b>sed<\/b><\/li>\n<p>Stream EDitor. A complete tool on its own, yet I use it mostly for small substitutions.<\/p>\n<p><code><br \/>\n$ cat 'foo bar baz' | sed -e 's\/foo\/quux\/'<br \/>\n<\/code><\/p>\n<p>Replaces foo with quux.<\/p>\n<li><b>xargs<\/b><\/li>\n<p>Run a command on every line of input on standard in.<\/p>\n<p><code><br \/>\n$ cat jars.txt | xargs -n1 sha1sum<br \/>\n<\/code><\/p>\n<p>Run sha1sum on every line in the file. This is another for loop or find -exec alternative. I use this when I have a long pipeline of commands in a oneliner and want to process every line in the end result.<\/p>\n<li><b>grep<\/b><\/li>\n<p>Here are some grep features you might not know:<\/p>\n<p><code>$ grep -A3 -B3 keyword data.txt<\/code><\/p>\n<p>This will list the match of the keyword in data.txt including 3 lines after (-A3) and 3 lines before (-B3) the match.<\/p>\n<p><code>$ grep -v keyword data.txt<\/code><\/p>\n<p>Inverse match. Match everything except keyword.<\/p>\n<li><b>sort<\/b><\/li>\n<p>Sort is another command often used at the end of a pipeline. For numerical sorting use<\/p>\n<p><code>$ sort -n<\/code><\/p>\n<li><b>Reverse search (CTRL-R)<\/b><\/li>\n<p>This one isn&#8217;t a real command but it&#8217;s really useful. Instead of typing history and looking up a previous command, press CTRL-R,<br \/>\nstart typing and have bash autocomplete your history. Use escape to quit reverse search mode. When you press CTRL-R your prompt will look like this:<\/p>\n<p><code>(reverse-i-search)`': <\/code><\/p>\n<li><b>!!<\/b><\/li>\n<p>Pronounced &#8216;bang-bang&#8217;. Repeats the previous command. Here is the cool thing:<\/p>\n<p><code>$ !!:s\/foo\/bar<\/code><\/p>\n<p>This repeats the previous command, but with foo replaced by bar. Useful if you entered a long command with a typo. Instead of manually replacing one of the arguments replace it this way.<\/p>\n<p><b>Bash script &#8211; checking artifacts in Nexus<\/b><\/p>\n<p>Below is the script I talked about. It loops over every jar and dll file in the current directory, calls Nexus via wget and optionally outputs a pom dependency snippet. It also adds a status column at the end of the output, either an OK or a KO, which makes the output easy to grep for further processing.<\/p>\n<pre class=\"brush: bash; title: ; notranslate\" title=\"\">\n#!\/bin\/bash\n\nok=0\njars=0\n\nfor jar in $(find $(pwd) 2&amp;&gt;\/dev\/null -name '*.jar' -o -name '*.dll')\ndo\n((jars+=1))\n\noutput=$(basename $jar)-pom.xml\nsha1=$(sha1sum $jar | awk '{print $1}')\n\nresponse=$(curl -s http:\/\/oss.sonatype.org\/service\/local\/data_index?sha1=$sha1)\n\nif &#x5B;&#x5B; $response =~ groupId ]]; then\n((ok+=1))\necho &quot;findjars $jar OK&quot;\necho &quot;&quot; &gt;&gt; &quot;$output&quot;\necho &quot;$response&quot; | grep groupId -A3 -m1 &gt;&gt; &quot;$output&quot;\necho &quot;&quot; &gt;&gt; &quot;$output&quot;\nelse\necho &quot;findjars $jar KO&quot;\nfi\n\ndone\n\nif &#x5B;&#x5B; $jars &gt; 0 ]]; then\necho &quot;findjars Found $ok\/$jars jars\/dlls. See -pom.xml file for XML snippet&quot;\nexit 1\nfi\n<\/pre>\n<p><b>Conclusions<\/b><\/p>\n<p>It is amazing what you can do in terms of scripting when you combine just these commands via pipes and redirection! It&#8217;s like a <a href=\"http:\/\/en.wikipedia.org\/wiki\/Pareto_principle\">Pareto&#8217;s law<\/a> of shell scripting, 20% of the features of bash and related tools provide 80% of the results. The basis of most scripts can be a for loop. Inside the for loop the resulting data can be transliterated, grepped, replaced by sed and finally run through another program via xargs.<\/p>\n<p><b>References<\/b><\/p>\n<p><a href=\"http:\/\/bashcookbook.com\/\">The Bash Cookbook<\/a> is a great overview of how to solve solutions to common problems using bash. It also teaches good bash coding style.<\/p>\n","protected":false},"excerpt":{"rendered":"<p>Introduction These days I spend a lot of time in the bash shell. I use it for ad-hoc scripting or driving several Linux boxes. In my current project we set up a continuous delivery environment and migrate code onto it. I lift code from CVS to SVN, mavenize Ant builds and funnel artifacts into Nexus. [&hellip;]<\/p>\n","protected":false},"author":6,"featured_media":0,"comment_status":"open","ping_status":"open","sticky":false,"template":"","format":"standard","meta":{"_acf_changed":false,"content-type":"","footnotes":""},"categories":[10,124],"tags":[331,48,9],"class_list":["post-7453","post","type-post","status-publish","format-standard","hentry","category-development","category-system-administration","tag-bash","tag-linux","tag-open-source"],"acf":[],"yoast_head":"<!-- This site is optimized with the Yoast SEO plugin v24.4 - https:\/\/yoast.com\/wordpress\/plugins\/seo\/ -->\n<title>Bash - A few commands to use again and again - Trifork Blog<\/title>\n<meta name=\"robots\" content=\"index, follow, max-snippet:-1, max-image-preview:large, max-video-preview:-1\" \/>\n<link rel=\"canonical\" href=\"https:\/\/trifork.nl\/blog\/bash-a-few-commands-to-use-again-and-again\/\" \/>\n<meta property=\"og:locale\" content=\"en_US\" \/>\n<meta property=\"og:type\" content=\"article\" \/>\n<meta property=\"og:title\" content=\"Bash - A few commands to use again and again - Trifork Blog\" \/>\n<meta property=\"og:description\" content=\"Introduction These days I spend a lot of time in the bash shell. I use it for ad-hoc scripting or driving several Linux boxes. In my current project we set up a continuous delivery environment and migrate code onto it. I lift code from CVS to SVN, mavenize Ant builds and funnel artifacts into Nexus. [&hellip;]\" \/>\n<meta property=\"og:url\" content=\"https:\/\/trifork.nl\/blog\/bash-a-few-commands-to-use-again-and-again\/\" \/>\n<meta property=\"og:site_name\" content=\"Trifork Blog\" \/>\n<meta property=\"article:published_time\" content=\"2013-03-28T17:42:42+00:00\" \/>\n<meta name=\"author\" content=\"frank\" \/>\n<meta name=\"twitter:card\" content=\"summary_large_image\" \/>\n<meta name=\"twitter:label1\" content=\"Written by\" \/>\n\t<meta name=\"twitter:data1\" content=\"frank\" \/>\n\t<meta name=\"twitter:label2\" content=\"Est. reading time\" \/>\n\t<meta name=\"twitter:data2\" content=\"5 minutes\" \/>\n<script type=\"application\/ld+json\" class=\"yoast-schema-graph\">{\"@context\":\"https:\/\/schema.org\",\"@graph\":[{\"@type\":\"WebPage\",\"@id\":\"https:\/\/trifork.nl\/blog\/bash-a-few-commands-to-use-again-and-again\/\",\"url\":\"https:\/\/trifork.nl\/blog\/bash-a-few-commands-to-use-again-and-again\/\",\"name\":\"Bash - A few commands to use again and again - Trifork Blog\",\"isPartOf\":{\"@id\":\"https:\/\/trifork.nl\/blog\/#website\"},\"datePublished\":\"2013-03-28T17:42:42+00:00\",\"author\":{\"@id\":\"https:\/\/trifork.nl\/blog\/#\/schema\/person\/00fad6c5829f6770345f23ccace2e54f\"},\"breadcrumb\":{\"@id\":\"https:\/\/trifork.nl\/blog\/bash-a-few-commands-to-use-again-and-again\/#breadcrumb\"},\"inLanguage\":\"en-US\",\"potentialAction\":[{\"@type\":\"ReadAction\",\"target\":[\"https:\/\/trifork.nl\/blog\/bash-a-few-commands-to-use-again-and-again\/\"]}]},{\"@type\":\"BreadcrumbList\",\"@id\":\"https:\/\/trifork.nl\/blog\/bash-a-few-commands-to-use-again-and-again\/#breadcrumb\",\"itemListElement\":[{\"@type\":\"ListItem\",\"position\":1,\"name\":\"Home\",\"item\":\"https:\/\/trifork.nl\/blog\/\"},{\"@type\":\"ListItem\",\"position\":2,\"name\":\"Bash &#8211; A few commands to use again and again\"}]},{\"@type\":\"WebSite\",\"@id\":\"https:\/\/trifork.nl\/blog\/#website\",\"url\":\"https:\/\/trifork.nl\/blog\/\",\"name\":\"Trifork Blog\",\"description\":\"Keep updated on the technical solutions Trifork is working on!\",\"potentialAction\":[{\"@type\":\"SearchAction\",\"target\":{\"@type\":\"EntryPoint\",\"urlTemplate\":\"https:\/\/trifork.nl\/blog\/?s={search_term_string}\"},\"query-input\":{\"@type\":\"PropertyValueSpecification\",\"valueRequired\":true,\"valueName\":\"search_term_string\"}}],\"inLanguage\":\"en-US\"},{\"@type\":\"Person\",\"@id\":\"https:\/\/trifork.nl\/blog\/#\/schema\/person\/00fad6c5829f6770345f23ccace2e54f\",\"name\":\"frank\",\"image\":{\"@type\":\"ImageObject\",\"inLanguage\":\"en-US\",\"@id\":\"https:\/\/trifork.nl\/blog\/#\/schema\/person\/image\/\",\"url\":\"https:\/\/secure.gravatar.com\/avatar\/5c39a948f2b70fa900b25dc79cde8643?s=96&d=mm&r=g\",\"contentUrl\":\"https:\/\/secure.gravatar.com\/avatar\/5c39a948f2b70fa900b25dc79cde8643?s=96&d=mm&r=g\",\"caption\":\"frank\"},\"url\":\"https:\/\/trifork.nl\/blog\/author\/frank\/\"}]}<\/script>\n<!-- \/ Yoast SEO plugin. -->","yoast_head_json":{"title":"Bash - A few commands to use again and again - Trifork Blog","robots":{"index":"index","follow":"follow","max-snippet":"max-snippet:-1","max-image-preview":"max-image-preview:large","max-video-preview":"max-video-preview:-1"},"canonical":"https:\/\/trifork.nl\/blog\/bash-a-few-commands-to-use-again-and-again\/","og_locale":"en_US","og_type":"article","og_title":"Bash - A few commands to use again and again - Trifork Blog","og_description":"Introduction These days I spend a lot of time in the bash shell. I use it for ad-hoc scripting or driving several Linux boxes. In my current project we set up a continuous delivery environment and migrate code onto it. I lift code from CVS to SVN, mavenize Ant builds and funnel artifacts into Nexus. [&hellip;]","og_url":"https:\/\/trifork.nl\/blog\/bash-a-few-commands-to-use-again-and-again\/","og_site_name":"Trifork Blog","article_published_time":"2013-03-28T17:42:42+00:00","author":"frank","twitter_card":"summary_large_image","twitter_misc":{"Written by":"frank","Est. reading time":"5 minutes"},"schema":{"@context":"https:\/\/schema.org","@graph":[{"@type":"WebPage","@id":"https:\/\/trifork.nl\/blog\/bash-a-few-commands-to-use-again-and-again\/","url":"https:\/\/trifork.nl\/blog\/bash-a-few-commands-to-use-again-and-again\/","name":"Bash - A few commands to use again and again - Trifork Blog","isPartOf":{"@id":"https:\/\/trifork.nl\/blog\/#website"},"datePublished":"2013-03-28T17:42:42+00:00","author":{"@id":"https:\/\/trifork.nl\/blog\/#\/schema\/person\/00fad6c5829f6770345f23ccace2e54f"},"breadcrumb":{"@id":"https:\/\/trifork.nl\/blog\/bash-a-few-commands-to-use-again-and-again\/#breadcrumb"},"inLanguage":"en-US","potentialAction":[{"@type":"ReadAction","target":["https:\/\/trifork.nl\/blog\/bash-a-few-commands-to-use-again-and-again\/"]}]},{"@type":"BreadcrumbList","@id":"https:\/\/trifork.nl\/blog\/bash-a-few-commands-to-use-again-and-again\/#breadcrumb","itemListElement":[{"@type":"ListItem","position":1,"name":"Home","item":"https:\/\/trifork.nl\/blog\/"},{"@type":"ListItem","position":2,"name":"Bash &#8211; A few commands to use again and again"}]},{"@type":"WebSite","@id":"https:\/\/trifork.nl\/blog\/#website","url":"https:\/\/trifork.nl\/blog\/","name":"Trifork Blog","description":"Keep updated on the technical solutions Trifork is working on!","potentialAction":[{"@type":"SearchAction","target":{"@type":"EntryPoint","urlTemplate":"https:\/\/trifork.nl\/blog\/?s={search_term_string}"},"query-input":{"@type":"PropertyValueSpecification","valueRequired":true,"valueName":"search_term_string"}}],"inLanguage":"en-US"},{"@type":"Person","@id":"https:\/\/trifork.nl\/blog\/#\/schema\/person\/00fad6c5829f6770345f23ccace2e54f","name":"frank","image":{"@type":"ImageObject","inLanguage":"en-US","@id":"https:\/\/trifork.nl\/blog\/#\/schema\/person\/image\/","url":"https:\/\/secure.gravatar.com\/avatar\/5c39a948f2b70fa900b25dc79cde8643?s=96&d=mm&r=g","contentUrl":"https:\/\/secure.gravatar.com\/avatar\/5c39a948f2b70fa900b25dc79cde8643?s=96&d=mm&r=g","caption":"frank"},"url":"https:\/\/trifork.nl\/blog\/author\/frank\/"}]}},"_links":{"self":[{"href":"https:\/\/trifork.nl\/blog\/wp-json\/wp\/v2\/posts\/7453","targetHints":{"allow":["GET"]}}],"collection":[{"href":"https:\/\/trifork.nl\/blog\/wp-json\/wp\/v2\/posts"}],"about":[{"href":"https:\/\/trifork.nl\/blog\/wp-json\/wp\/v2\/types\/post"}],"author":[{"embeddable":true,"href":"https:\/\/trifork.nl\/blog\/wp-json\/wp\/v2\/users\/6"}],"replies":[{"embeddable":true,"href":"https:\/\/trifork.nl\/blog\/wp-json\/wp\/v2\/comments?post=7453"}],"version-history":[{"count":0,"href":"https:\/\/trifork.nl\/blog\/wp-json\/wp\/v2\/posts\/7453\/revisions"}],"wp:attachment":[{"href":"https:\/\/trifork.nl\/blog\/wp-json\/wp\/v2\/media?parent=7453"}],"wp:term":[{"taxonomy":"category","embeddable":true,"href":"https:\/\/trifork.nl\/blog\/wp-json\/wp\/v2\/categories?post=7453"},{"taxonomy":"post_tag","embeddable":true,"href":"https:\/\/trifork.nl\/blog\/wp-json\/wp\/v2\/tags?post=7453"}],"curies":[{"name":"wp","href":"https:\/\/api.w.org\/{rel}","templated":true}]}}