2

I am trying to write a script that will take in an inputFile, convert it to lowercase, sort it, and then store the results back in the original file. I'm fairly new to bash, so this is the solution I've come up with so far:

awk '{ print tolower($0) }' $inputFile

index=0
for i in `cat $inputFile`
do                   
    tables[${index}]=$i
    index=$(($index + 1))
done

IFS=$'\n' tables=($(sort <<<"${tables[*]}"))
rm -r $inputFile
printf "%s\n" "${tables[@]}" >> $inputFile

The sorting aspect of this works fairly well, but I am unable to store the results of awk to the original inputFile, so the sorted table still includes uppercase letters. I have tried to redirect the output of awk to > inputFile, but this didn't work either.

Sample inputFile:

TABLE.thisisaTABLE
taBLe.hellO
HELLO.table
hi.table

Desired output (back into original inputFile):

hello.table
hi.table
table.hello
table.thisisatable
raphnguyen
  • 3,565
  • 18
  • 56
  • 74
  • it looks interesting and there are few things to be improved (`while read line; do ... done < file` is preferred over `for i in cat...`). Could you indicate a sample input file with the desired output? – fedorqui Feb 06 '15 at 19:22
  • That usage of `IFS` is **not** local to that one line. – Etan Reisner Feb 06 '15 at 19:25
  • @fedorqui I've updated the OP to include the info. – raphnguyen Feb 06 '15 at 19:26
  • What version of bash are you targetting? – Etan Reisner Feb 06 '15 at 19:29
  • @EtanReisner I'm fairly unfamiliar with `IFS` so you might have to explain what I'm doing wrong there. I'm using bash `4.1.2` – raphnguyen Feb 06 '15 at 19:30
  • 3
    Why not just `awk '{ print tolower($0) }' $inputFile | sort -o $inputFile`? Is there other processing your're expecting to do while sorting? – n0741337 Feb 06 '15 at 19:31
  • A line like `FOO=bar some_command` normally assigns `FOO` a value *only* for that command. `IFS` doesn't work that way though. So that assignment to `IFS` remains in effect for the rest of your script. – Etan Reisner Feb 06 '15 at 19:31
  • Does `awk '{map[tolower($0)]++} END {for (line in map) {print line}}' $inputFile | sort` do what you want? – Etan Reisner Feb 06 '15 at 19:32
  • @n0741337 simply because I didn't know any better. This works great! Please submit it as an answer so that I can accept. @EtanReisner thanks for the tip about `IFS`. I'll be cautious about its use in the future. – raphnguyen Feb 06 '15 at 19:37
  • @EtanReisner Your statement about `IFS` is wrong: `IFS=test ls; declare -p IFS`. – gniourf_gniourf Feb 06 '15 at 22:17
  • echo "Poor old tr. How quickly you are Forgotten." | tr '[:upper:]' '[:lower:]' – msw Feb 07 '15 at 21:33
  • @gniourf_gniourf Try the OP's example. `IFS=test foo=bar; declare -p IFS`. So apparently it is more nuanced then I had previously thought. I would have sworn that IFS behaved badly in many cases but the cases seem to be much more limited then I believed. The OP's happens to be among them though. But thanks for the correction. – Etan Reisner Feb 08 '15 at 22:34
  • @EtanReisner It's just a regular assignment: try `a=b c=d e=f g=h` and observe that assignments don't need to be separate statements. In OP's case, it's just what happens. `IFS` isn't special at all. – gniourf_gniourf Feb 08 '15 at 22:58
  • @gniourf_gniourf Indeed (I still feel like there was something else with `IFS` but have no proof at the moment). Anyway, it just so happens that most assignments don't have the same side-effects as `IFS` so don't cause the same problems. Ultimately, my comment should probably have just been "You are setting IFS for the whole rest of the script on that line. You probably don't want to do that". – Etan Reisner Feb 09 '15 at 02:35

3 Answers3

3

You can use the -o flag of sort to perform both the sort and the redirection back into the original file:

awk '{ print tolower($0) }' $inputFile | sort -o $inputFile
n0741337
  • 2,474
  • 2
  • 15
  • 15
  • I could be wrong but that does not look safe to me and I wouldn't put money on `$inputfile` not being overwritten before `sort` is finished. Yes, `sort -o` lets you specify the same name as an input file and guarantees it's safety but that is not what you're doing with that script so `sort` may think it's OK to init the output file before it's finished reading all it's input. – Ed Morton Feb 06 '15 at 20:24
  • @EdMorton - Good point; probably depends on the sort implementation. For example, this [GNU sort manual page](https://www.gnu.org/software/coreutils/manual/html_node/sort-invocation.html) says its safe unless you're using the `--merge(-m)` flag. – n0741337 Feb 06 '15 at 21:30
1

Similar solution with sed:

sed 's/.*/\L&/' $inputFile | sort -o $inputFile

Explanation: s/.*/\L&/ means convert entire line (.*) to lower case using \L. & stands for the matched pattern.

Arjun Mathew Dan
  • 5,240
  • 1
  • 16
  • 27
0

You can use Perl:

$ perl -lne 'push @a, lc; END { print join("\n", sort @a) }' $inputFile
hello.table
hi.table
table.hello
table.thisisatable

Works by:

perl -lne      # invoke perl with a loop around the lines of the file
push @a, lc;   # make line read lower case; push the result
END            # block of code executed at the end
{ print join("\n", sort @a) }   # Print each sorted line with \n 

If you want to modify the file in place:

$ perl -i.bak -0777 -lne 'print join("\n", sort map(lc, split /\n/))' file.txt

And if you don't want a backup file made:

$ perl -i -0777 -lne 'print join("\n", sort map(lc, split /\n/))' file.txt