0

I want to convert the coding of some csv-files with iconv. It has to be a script so I am working with while; do done. The script lists every item in a specific directory and converts them into another coding (utf-8).

Currently, my script lists EVERY item, including directories... So here are my questions

  1. Does iconv has a problem with directories or does it ignore them?

  2. And if there is a problem, how can I only list/search only for files?

I tried How to list only files in Bash? a ***./*** at the beginning of every item and that's kinda annoying (and my program doesn't like it, too).

Another possibility is ls -p | grep -v / but this would also affect files with / in the name, wouldn't it?

I hope you can help me. Thank you.


Here is the code:

for item in $(ls directory/); do
   FileName=$item
   iconv -f "windows-1252" -t "UTF-8" FileName -o FileName
done

Yea, i know, the input and output file cannot be the same^^

jww
  • 97,681
  • 90
  • 411
  • 885
Telvanis
  • 55
  • 1
  • 8
  • First, *do not parse the output of `ls`.* Second... how come your title mentions a good approach totally absent from your question? (Links don't really count...) – DevSolar May 29 '18 at 10:59
  • Plus, as far as I can see iconv does not *have* an option `-o`... – DevSolar May 29 '18 at 11:05
  • Sorry, u are right. I do have a problem with find . -maxdepth 1 -type f , but thats not the main one. I edited the title. – Telvanis May 29 '18 at 11:06
  • [Related answer](https://stackoverflow.com/a/9930780/60281) showing what you can do with `find ... -exec`. That should always be the preferred route, because it doesn't balk at things like spaces or other funny things like newlines in the filename. – DevSolar May 29 '18 at 11:08
  • "Plus, as far as I can see iconv does not have an option " Well ok, but I used it with this options and it works perfectly and as expected. The only problem is that I have to feed this command with a list of csv-files... – Telvanis May 29 '18 at 11:08
  • Generally speaking, posting the exact problem you have with *one* approach (preferrably, the `find ... -exec` based one) and asking about that would be the better question. Right now you're asking "can you write an solution for me?", and that is usually not a good thing. -- You say your program "doesn't like the `./` at the beginning". What do you mean, "doesn't like"? – DevSolar May 29 '18 at 11:10
  • It seems like I expressed it badly :/ to be clear; I do note want and need a "write a solution for me"... I have a php-file working with those results and the ./ are annoying. Yea, I can cut it, but maybe there is an answer which is more elegant. The main problem is solved, iconv does NOT have a prolem with directories. – Telvanis May 29 '18 at 11:24
  • What "results" does the PHP script pick up? The filenames? I thought these are processed by `iconv`? If you need the *filenames themselves* without the leading `./`, try `find . -maxdepth 1 -type 1 -printf "%f"`. Caveats regarding whitespace in filenames applies. – DevSolar May 29 '18 at 11:25

2 Answers2

0

Building upon the existing question that you referenced, Why don't you just remove the first 2 characters i.e. ./?

  find . -maxdepth 1 -type f | cut -c 3-

Edit: I agree with @DevSolar about the space-based problem in the for-loop. While I think that his solution is better for this problem, I just want to give an alternative way to get out of the space-based for-loop issue.

   OLD_IFS=$IFS
   IFS=$'\n'
   for item in $(find . -maxdepth 1 -type f | cut -c 3-); do
   FileName=$item
   iconv -f "windows-1252" -t "UTF-8" FileName -o FileName
   done
   IFS=$OLD_IFS
UchihaItachi
  • 2,602
  • 14
  • 21
  • @DevSolar, Can you give an example of a file name where it breaks? – UchihaItachi May 29 '18 at 11:24
  • hm well yea that would be a possiblity and I guess, I'm gonna do it like that. I just don't rly like it to hardcode a cut after a specfic amount of characters.... It seems so error-prone. Thank you :) – Telvanis May 29 '18 at 11:27
  • Depends on what he feeds the result of that command to. `for file in $(find . -maxdepth 1 -type f | cut -c 3-); do echo $file; done` with filename "foo bar.txt" gives one "foo", one "bar.txt", and two "file not found" when you attempt any operation on those "files". ;-) – DevSolar May 29 '18 at 11:29
  • @Telvanis: Note my comment on using `-printf "%f"` if you want *just* the filename. Generally speaking, *do* read `man find`. That tool can do much more for you than you think, and beats virtually all other attempts at iterating over a set of files. – DevSolar May 29 '18 at 11:34
0

Use find directly:

find . -maxdepth 1 -type f -exec bash -c 'iconv -f "windows-1252" -t "UTF-8" $1 > $1.converted && mv $1.converted $1' -- {} \;
  • find . -maxdepth 1 -type f finds all files in the working directory
  • -exec ... executes a command on each such file (including correct handling of e.g. spaces or newlines in the filename)
  • bash -c '...' executes the command in '...' in a subshell (easier to do the subsequent steps, involving multiple expansions of the filename, this way)
  • -- terminates option processing, and treats anything after the -- as arguments to the call.
  • {} is replaced by find with the file name(s) found
  • $1 in the bash command is replaced with the first (and only) argument, which is the {} replaced by the filename (see above)
  • \; tells find where the -exec'ed command ends.
DevSolar
  • 67,862
  • 21
  • 134
  • 209