9

I don't know how to execute a command with a variable and get the result of this. I have many .h and .c files and I need convert each from ISO-8859 to UTF-8.

So I make:

ls | grep "\.[ch]" | xargs myscript.sh

And in my script, the filename is in variable $1. Now I need to perform

iconv -f ISO-8859 -t UTF-8 $1

and store result of this, because iconv prints to stdout.

result=`iconv -f ISO-8859 -t UTF-8 $1`

echo $result

This seems to be not working, because it gives me some mismatch instead of converted $1.

Peter Mortensen
  • 30,738
  • 21
  • 105
  • 131
designerrr
  • 222
  • 1
  • 2
  • 8
  • 3
    "Some mismatch"? Please quote the exact error message you're getting. – hmakholm left over Monica Nov 29 '11 at 22:40
  • 1
    It is not error. It is srabled content of more files then only $1, i dont understand that. – designerrr Nov 29 '11 at 22:44
  • do any of the files emitted from you `ls | grep ..` have spaces in the filename? That will blow things up. Try changing to"$1" in your `result=...`, but you may need to come up with a way to handle spaces in file names. Please edit your question with approriate example values for $1 AND the exact text of scramble content. Good luck. – shellter Nov 29 '11 at 23:15
  • If your files are small enough to capture, then you need to use `echo "$result"` with the double quotes to preserve the internal spacing. Without the double quotes, you get a single line of output with each sequence of one or more white spaces reduced to a single blank. – Jonathan Leffler Nov 30 '11 at 20:44
  • The canonical is *[How do I set a variable to the output of a command in Bash?](https://stackoverflow.com/questions/4651437)*. Despite the unspecific title, it covers the case of ***(variable) input to the external command*** (in a (Bash) variable. – Peter Mortensen Nov 10 '20 at 00:57

3 Answers3

8

If you need to do some kind of transformation on the data first, you can "capture" output with the following syntax:

result="$(iconv -f ISO-8859 -t UTF-8 $1)"

There is a gotcha here as well: if you are going to be storing large amounts of data with potential whitespace or other meddlesome characters in it, be sure to always quote the variable ("$result" instead of $result) to ensure it gets treated as a single string.

Michael
  • 880
  • 2
  • 6
  • 16
  • This `$cmd $1 > $1` will not just overwrite the file, it will _clobber_ it **first**. This is a good way to truncate all your source files. – sehe Nov 29 '11 at 22:47
  • Fixed again. Man, my shell is rusty. – Michael Nov 29 '11 at 22:52
  • Ok, -1 removed :) **P.S.:** these are presumably C source files, so yes, onless it was the submission for an obfuscated code contest, there is _definitely_ going to be _some_ whitespace in there :) – sehe Nov 29 '11 at 22:53
  • Thank you man, it give me some mismatch because i dont quote $result when i echoes it. – designerrr Nov 29 '11 at 23:00
3

I'd do as such:

while read filename; 
do
    mv "$filename" "$filename.bck" && \
        iconv -f ISO-8859 -t UTF-8 "$filename.bck" > "$filename"
done < find -iname '*.[hc]'

This creates backups on the fly and also handles files with whitespace (not newline characters).

sehe
  • 374,641
  • 47
  • 450
  • 633
3

Here is a solution that even handles newlines:

find -name '*.[ch]' \
    -exec mv '{}' '{}.backup' \; \
    -exec iconv -f ISO-8859 -t UTF-8 '{}.backup' -o '{}' \;

Generally, never parse filenames if you are going to use the results. The only sane ways I know of are

  1. Use shell globs, e.g. for file in ./*.[ch] ; do echo "$file" ; done. Only works for one directory.
  2. Use find in combination with -exec
  3. Use 'find' in combination with -print0 (which prints the filenames as \0-separated strings) and use the output to build commandlines with xargs -0 and probably a helper script. This is quite cumbersome, though.

Also, make sure that relative filenames you use are prefixed with ./. Calling mv -from -to isn't safe, but mv ./-from ./-to is, and does what you want. E.g. when globbing, go with ./*.c rather than *.c.

Peter Mortensen
  • 30,738
  • 21
  • 105
  • 131
Jo So
  • 25,005
  • 6
  • 42
  • 59