Differences

This shows you the differences between two versions of the page.

Link to this comparison view

Next revision
Previous revision
howto:dissectabadoneliner [2009/04/09 04:22]
127.0.0.1 external edit
howto:dissectabadoneliner [2015/08/09 05:04] (current)
bill_thomson
Line 8: Line 8:
 <code bash>$ ls *.zip | while read i; do ...; done</​code>​ <code bash>$ ls *.zip | while read i; do ...; done</​code>​
  
-(Please read [[http://​mywiki.wooledge.org/​ParsingLs]].) This command executes ''​ls''​ on the expansion of ''​*.zip''​. Assuming there are filenames in the current directory that end in '​.zip',​ ls will give a human-readable list of those names. The output of ls is not for parsing. But in sh and bash alike we can loop safely over the glob itself:+(Please read [[http://​mywiki.wooledge.org/​ParsingLs]].) This command executes ''​ls''​ on the expansion of ''​*.zip''​. Assuming there are filenames in the current directory that end in '​.zip',​ ls will give a human-readable list of those names. The output of ls is not for parsing. But in sh and bash alikewe can loop safely over the glob itself:
  
 <code bash>$ for i in *.zip; do j=`echo $i | sed '​s/​.zip//​g'​`;​ mkdir $j; cd $j; unzip ../$i; cd ..; done</​code>​ <code bash>$ for i in *.zip; do j=`echo $i | sed '​s/​.zip//​g'​`;​ mkdir $j; cd $j; unzip ../$i; cd ..; done</​code>​
Line 16: Line 16:
 <code bash>​j=`echo $i | sed '​s/​.zip//​g'​` # where $i is some name ending in '​.zip'</​code>​ <code bash>​j=`echo $i | sed '​s/​.zip//​g'​` # where $i is some name ending in '​.zip'</​code>​
  
-The goal here seems to be just get the filename without its ''​.zip''​ extension. In fact, there is a POSIX(r)-compliant command to do this already: ''​basename''​The implementation here is suboptimal in several ways, but the only thing that's genuinely error-prone with this is "''​echo $i''"​. Echoing an //​unquoted// ​value means that [[syntax:​expansion:​wordsplit | wordsplitting]] will take place, so any whitespace in ''​$i''​ will essentially be normalized. In ''​sh''​ it is necessary to use an external command and a subshell to achieve the goal, but we can eliminate ​pipe (subshells, external commands, and pipes carry extra overhead when they launch, so they can really hurt performance in a loop). Just for good measure, let's use the more readable, [[syntax:​expansion:​cmdsubst | modern]] ''​$()''​ instead of oldschool ​backticks:+The goal here seems to be get the filename without its ''​.zip''​ extension. In fact, there is a POSIX(r)-compliant command to do this: ''​basename''​ The implementation here is suboptimal in several ways, but the only thing that's genuinely error-prone with this is "''​echo $i''"​. Echoing an //​unquoted// ​variable ​means [[syntax:​expansion:​wordsplit | wordsplitting]] will take place, so any whitespace in ''​$i''​ will essentially be normalized. In ''​sh''​ it is necessary to use an external command and a subshell to achieve the goal, but we can eliminate ​the pipe (subshells, external commands, and pipes carry extra overhead when they launch, so they can really hurt performance in a loop). Just for good measure, let's use the more readable, [[syntax:​expansion:​cmdsubst | modern]] ''​$()'' ​construct ​instead of the old style backticks:
  
 <code bash>sh $ for i in *.zip; do j=$(basename "​$i"​ "​.zip"​);​ mkdir $j; cd $j; unzip ../$i; cd ..; done</​code>​ <code bash>sh $ for i in *.zip; do j=$(basename "​$i"​ "​.zip"​);​ mkdir $j; cd $j; unzip ../$i; cd ..; done</​code>​
Line 28: Line 28:
 <code bash>$ mkdir $j; cd $j; ...; cd ..</​code>​ <code bash>$ mkdir $j; cd $j; ...; cd ..</​code>​
  
-As a programmer, you **never** know the situation under which your program will run. Even if you do, the following best practice will never hurt: Whenever your following command depends on the success of your previous command(s), check for success! You can do this with the "''&&''"​ conjunction, ​which means that if the previous command fails, bash will not try to execute the following command(s). It's fully POSIX(r). Oh, and remember what I said about [[syntax:​expansion:​wordsplit | wordsplitting]] in the previous step? Well, if you don't quote ''​$j'',​ wordsplitting can happen again.+As a programmer, you **never** know the situation under which your program will run. Even if you do, the following best practice will never hurt: When a following command depends on the success of previous command(s), check for success! You can do this with the "''&&''"​ conjunction,​ that way, if the previous command fails, bash will not try to execute the following command(s). It's fully POSIX(r). Oh, and remember what I said about [[syntax:​expansion:​wordsplit | wordsplitting]] in the previous step? Well, if you don't quote ''​$j'',​ wordsplitting can happen again.
  
 <code bash>$ mkdir "​$j"​ && cd "​$j"​ && ... && cd ..</​code>​ <code bash>$ mkdir "​$j"​ && cd "​$j"​ && ... && cd ..</​code>​
Line 48: Line 48:
 <code bash>​mkdir "​$j"​ && cd "​$j"​ && unzip ../$i && cd -</​code>​ <code bash>​mkdir "​$j"​ && cd "​$j"​ && unzip ../$i && cd -</​code>​
  
-Well, besides ​the wordsplitting, there'​s nothing terribly wrong with this. Still, did it occur to you that unzip might already be able to target a directory? There isn't a standard for the ''​unzip''​ command, but all the implementations I've seen can do it with the -d flag. So we can drop the cd commands entirely:+Well, besides ​word splitting, there'​s nothing terribly wrong with this. Still, did it occur to you that unzip might already be able to target a directory? There isn't a standard for the ''​unzip''​ command, but all the implementations I've seen can do it with the -d flag. So we can drop the cd commands entirely:
  
 <code bash>$ mkdir "​$j"​ && unzip -d "​$j"​ "​$i"</​code>​ <code bash>$ mkdir "​$j"​ && unzip -d "​$j"​ "​$i"</​code>​
  • howto/dissectabadoneliner.1239250958.txt
  • Last modified: 2015/08/09 05:04
  • (external edit)