Take the 2-minute tour ×
Unix & Linux Stack Exchange is a question and answer site for users of Linux, FreeBSD and other Un*x-like operating systems.. It's 100% free, no registration required.

Suppose I have a subdirectory called sub. Then I want to operate on all files in that directory with the extension "txt". I want something like

for f in sub/*.txt
do
    enscript -b "" -o {$f basename excluding .txt}.ps sub/{$f basename excluding .txt}.txt                                                                                                                             
    ps2pdf {$f basename excluding .txt}.ps {$f basename excluding .txt}.pdf
done

So if there was just one file in the sub directory of the form *.txt, say sub/foo.txt, the script should expand to

enscript -b "" -o foo.ps sub/foo.txt                                                                                                                                   
ps2pdf foo.ps foo.pdf

I generally prefer portable solutions, but it is always interesting to learn about different ways of doing things. So non-portable is Ok, please just mention whether your solution is portable or non-portable. Also, the title is probably not the best possible, so feel free to modify.

EDIT: I realized the example as written did not quite make sense, since foo.txt was in the sub directory, and therefore sub/foo.txt would need to be passed to enscript. I also changed the -p enscript option to the more standard -o option.

share|improve this question

2 Answers 2

up vote 4 down vote accepted

POSIX shells (e.g., bash, dash, ksh, …) accept a ${var%suffix} construct that can be used to remove the trailing portion from the value of a variable. For instance, if path="sub/file.txt", then ${path%.txt} expands to sub/file.

There's a symmetric construct to remove a prefix: ${var#prefix}. The prefix can be a pattern. Doubling the # strips the longest matching prefix. For example, if path=sub/dir/file.txt, then ${path##*/} expands to file.txt (and ${path#*/} expands to dir/file.txt). The same goes for suffixes and %.

So, in your case you could write this (note that you can't combine the prefix stripping and the suffix stripping into a single expansion, at least not with only POSIX constructs):

for f in sub/*.txt; do
  base=${f%.txt}
  base=${base##*/}
  enscript -b "" -o "$base.ps" "$f"
  ps2pdf "$base.ps" "$base.pdf"
done

Alternatively, the GNU basename command accepts an optional second argument which is the file extension to remove. For instance, if $f is file.txt, then $(basename $f .txt) expands to file.

Note, however, that basename removes all path information except for the last component, so if you want to remove just the extension you have to put that back (see the dirname command).

share|improve this answer
    
@Riccardo: Wow, that was fast. Thanks for replying. A comment and a question. 1) the link for basename has a typo (/ instead of .) and 2) these are links to what look like C utilities. Am I missing something? I should have said that my general preference for shell scripts is portability, so the ${path%.txt} will do me. I'm guessing the GNU basename thing, whatever it is. is not portable. –  Faheem Mitha May 30 '11 at 20:29
    
@Riccardo: I think your script has an error. Run exactly as written I get the error: syntax error near unexpected token prefix=${f%.txt}'. If I take out the semi-colon and add a do` it works. –  Faheem Mitha May 30 '11 at 20:42
    
@Gilles: You forgot to fix the basename url. :-) –  Faheem Mitha May 30 '11 at 21:00
    
@Faheem Mitha: Fixed the links. I guess the Gilles fixed the script already, as I can run it correctly? –  Riccardo Murri May 30 '11 at 21:03
    
@Riccardo: He did, yes. It just needed the addition of a do. Thanks. –  Faheem Mitha May 30 '11 at 21:12

The portable way is to strip the extension with ${f%.*} and the directory with ${f##*/} (or use basename).

In zsh, you can apply various transformations to parameter expansion, including history modifiers such as r to strip one extension and t to strip the directory part.

for x in sub/*.txt; do
    enscript -b "" -o ${x:t:r}.ps $x
    ps2pdf ${x:t:r}.ps ${x:t:r}.pdf
done

You can apply history modifiers to glob patterns as well.

for x in sub/*.txt(:r:t); do
    enscript -b "" -o $x.ps sub/$x.txt
    ps2pdf $x.ps $x.pdf
done
share|improve this answer
    
Like @Riccardo's version, this does not strip the leading sub/. As commented above, basename=${prefix#sub/} works, along the same lines as @Riccardo's approach. –  Faheem Mitha May 31 '11 at 8:11
    
well, zsh is clearly handy. Thanks. –  Faheem Mitha May 31 '11 at 11:53
    
I've made some corrections to my script in the question. Just mentioning it in case you feel like adjusting your answer. –  Faheem Mitha May 31 '11 at 16:36

Your Answer

 
discard

By posting your answer, you agree to the privacy policy and terms of service.

Not the answer you're looking for? Browse other questions tagged or ask your own question.