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.

Possible Duplicate:
Recursive rename files and directories

I wrote the following script:

#!/bin/bash
SAVEIFS=$IFS
alias export='export'
IFS=$(echo -en "\n\b")

          find $1 -name "*" -a -type f -exec sh -c let  len=`expr length {}` \;  -exec  sh -c let  str=`expr substr {} 1 len-3` \; -exec ffmpeg -y -i {}  $str.mp3 \;

# restore $IFS
unalias export
IFS=$SAVEIFS

Problem:

You know when more one shell you can't export your variable in other shell, So i need to :

  1. use variables

  2. don't use run shell

So, how i do it?

When run the following script:

find $1 -name "*" -a -type f -exec let  len=`expr length {}` \;  -exec let  str=`expr substr {} 1 len-3` \; -exec ffmpeg -y -i {}  $str.mp3 \; 
# Note : above script doesn't has 'sh -c' 

I get the following error:

find: `let': No such file or directory

I tested it with export or delete export or let , i discovered -exec has problem with built-in shell command....!

Do you have any idea????

share|improve this question

marked as duplicate by Gilles, Renan, uther, Shadur, jasonwryan Jan 9 '13 at 20:51

This question has been asked before and already has an answer. If those answers do not fully address your question, please ask a new question.

1  
Can you sum up what you want to accomplish with your script? What's the input and the desired output? –  Herman Torjussen Jan 8 '13 at 20:16
    
My input set of *.3gp files, and i convert them to *.mp3 files, in first step of script i wrote successfully, but when i convert, suppose i do have myfile.3gp and when i convert it renamed to myfile.3gp.mp3 , i used expr for solve it. –  Mohsen Pahlevanzadeh Jan 8 '13 at 20:34

2 Answers 2

up vote 2 down vote accepted

You need to take care of several things

  • double quotes to protect the content of a variable from shell interpretation. IFS contain probably by default "space+tab+newline", so : NEWIFS=IFS is probably going to end up putting nothing in NEWIFS.

    SAVEIFS="$IFS"
    ...
    IFS="$(echo -en "\n\b")"   
    #yes, you can have " inside $( ... ) : the shell will gladly interpret them by
    # order of depth inside the $() construct. 
    # A big advantage over using backquotes where it would get mixed up and almost unreadable
    ...
    IFS="$SAVEIFS"
    
  • Don't use reserved words as variable names (avoid for let etc. Avoid also many "common" variable names, ie LINES, COLUMNS, etc.

    • why aliasing export to export? looks like a good way to create a loop if the shell is old, or at best it's useless.

    • precising -name * means you avoid any filename or dirnames starting with a ..

    • -exec sh .... will launch it in an sh subshell: any variables you declare in there will be lost once that sh exits out to the invoking shell.

You need something like:

#!/bin/bash
for adirectory in "$@" ; do
    find "$adirectory" -type f -name '*.3gp' -print | while IFS= read -r wholeline ; do
       mylength=$(echo "$wholeline" | wc -c)  #Not needed, just kept as exemple of a way to achieve it
       myfilewithoutext="$(echo "$wholeline" | sed -e 's/.3gp$//' )"  
          #keeps only the filename without its ".3gp" or ".3GP" extension.
       ffmpeg -y -i "$wholeline"  "${myfilewithoutext}.mp3"    #?  please check if it works
    done #end of the find|while
done #end of the for looping on all the arguments passed to the script (dir names)

IFS in that while IFS= read -r wholeline ; do will be set to the empty string ONLY during the invocation of the read -r wholeline. Untouched outside of it. VAR="something" command parameters will invoke command parameters, with the value of VAR temporarily set to something. Here we set IFS to '', i.e. nothing. It will still be passed line by line, and as IFS is empty the whole line ends up read by read wholeline.

share|improve this answer
    
while IFS= read -r line has come up several times, I think Why is while IFS= read used so often, instead of IFS=; while read..? is our definitive post on the subject. I haven't reviewed your script fully. These are good explanations. The script is still more complex and brittle than it needs to be, in particular the call to sed should be replaced by built-in shell constructs: myfilewithoutext=${wholeline#.3gp}. –  Gilles Jan 8 '13 at 23:19
    
@Gilles: I use bash versions that are so old they can't handle those "fancy" constructs ^^ . I try to keep it portable (albeit, I agree, at the expense of sometimes having to do some ugly hacks). Thanks for the reformatting, I'll edit again to weed out old stuff (like my questions about the extension, which were answered by other comments) –  Olivier Dulac Jan 8 '13 at 23:31
    
AFAIR bash has always supported ${…#…} and friends. It's in POSIX. Most Bourne shells don't have it, but systems without a POSIX shells are getting far and few. –  Gilles Jan 8 '13 at 23:36
    
Thanks for that heads-up, I'll have a look to see if our system's bash supports it. But it's old (version 2.0.something) (no "$LINENO", no "$BASH_SOURCE", etc...) –  Olivier Dulac Jan 8 '13 at 23:42
    
${VAR#PREFIX} was already supported in bash 1.14. I can't easily find information about earlier versions. In BSD, it seems to have appeared only in 4.4BSD, so some time during the FreeBSD-2.x/NetBSD-1.x series (according to the manpages). –  Gilles Jan 9 '13 at 0:03

As per comment (converting *.3gp to *.mp3):

for n in *.3gp; do
    ffmpeg -y -i "$n"  "${n%.3gp}.mp3"
done

As per olivier's comment, to also act on files in subdirectories, *.3gp can be replaced with $(find ...), provided the filenames don't need escaping (namely they don't contain whitespace or globbing characters \[?*). Alternatively **/*.3gp can be used (in bash, put shopt -s extglob in your .bashrc).

share|improve this answer
1  
neat approach. easy to read, and nice handling of the spaces in filenames. A caveat: won't find the 3gp file in the subdirs. Actually it find them in "the current dir, and only the first level" instead of "all files underneath directory $1" as @Mohsen Pahlevanzadeh apparently intended... –  Olivier Dulac Jan 8 '13 at 22:29
    
@OlivierDulac good point :) –  peterph Jan 8 '13 at 22:59

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