4

Please would someone be able to explain how I can convert all the lower case characters in a text file to upper case and then save it as a new file? My file is called NewFile.txt and contains 500 lines of random characters.

1

5 Answers 5

25

In the POSIX toolchest, there's:

<input.txt tr '[:lower:]' '[:upper:]' >output.txt

However note that with the GNU implementation, that only works for single-byte characters; so in locales using the UTF-8 charset for instance, only on abcdefghijklmnopqrstuvwxyz letters without diacritics.

<input.txt awk '{print toupper($0)}' >output.txt

is also POSIX and works OK with the GNU implementation of awk.

<input.txt dd conv=ucase >output.txt

is also POSIX but not many implementations will transliterate non-ASCII characters.

<input.txt sed 's/.*/\U&/g' > output.txt

Works in GNU sed, but GNU sed only (that \U is not standard).

With perl:

<input.txt perl -Mopen=locale -pe '$_=uc' >output.txt

That one doesn't use the locale's toupper rules, so may work better on words like office (converting that one character to the three character FFI¹).

uconv, from the ICU project should be pretty good at handling all sorts of international corner cases, and assuming input / output encoded in UTF-8 (or whatever uconv --default-code returns; though see the -f/--from-code and -t/--to-code options to specify different input and output encodings):

<input.txt uconv -x upper >output.txt

Within the vim editor, if on the first character of the file (gg to get there), enter gUG to convert all to uppercase til the end of the file. Then :saveas output.txt to save to output file.

Or with any ex or vi implementation (though not all will handle non-ASCII characters): :%s/.*/\U&/ (and :w output.txt to write the edited file to output.txt and :q! to quit without saving the now modified input file).

With the zsh shell:

zmodload zsh/mapfile
mapfile[output.txt]=${(U)mapfile[input.txt]}
# or (csh-style):
mapfile[output.txt]=$mapfile[input.txt]:u

To convert from upper to lower case instead, in case that's not already obvious:

  • tr: swap [:lower:] and [:upper:]
  • awk: change toupper to tolower
  • dd: change ucase to lcase
  • GNU sed / ex / vi: change \U to \L
  • perl: change uc to lc.
  • uconv: change upper to lower
  • vim: change gUG to guG (that's the trick one).
  • zsh: change (U) to (L), :u to :l.

¹ the C / POSIX toupper() / towupper() API only converts one character to another one at a time, so is limited in how it can change the case of text. See https://unicode-org.github.io/icu/userguide/icu/posix.html#case-mappings about that and more.

0
3

A bash one(ish) liner using only builtins...

f="$(< infile.txt)" ; printf "%s" "${f^^}" > outfile.txt ; unset f

We fill temporary variable f with the content of infile.txt.

Then we print f to STDOUT while using bash variable trickery to uppercase it (${f^^}) and redirect STDOUT to outfile.txt.

Caveat: Probably going to play up if the "randomness" deviates from Latin and printable characters. Also drops any trailing newlines.

Per comments, this is probably better (more efficient, no newline dropping):

readarray f < infile.txt ; printf %s "${f[@]^^}" > outfile.txt ; unset f
6
  • 1
    Also note that it removes all the trailing newline characters (including the one delimiting the last line if non empty), so would in effect make outfile.txt non-text. It also removes NUL characters (though those are not meant to occur in txt files) Commented Aug 17, 2021 at 11:29
  • Using IFS= read -rd '' f < infile.txt would probably be better (avoids the fork, though may still be inefficient as read reads by small chunks, and the trimming of newlines; would stop at the first NUL though). Commented Aug 17, 2021 at 11:31
  • Another option would be readarray f < infile.txt; printf %s "${f[@]^^}" (also assuming no NUL in the input, but likely more efficient than read or $(<file)). Commented Aug 17, 2021 at 12:50
  • Huh, somehow I never noticed that $( ) dropped trailing newlines. Thanks for the heads up.
    – bxm
    Commented Aug 17, 2021 at 12:54
  • FWIW, bash's ${var^^} should be OK for characters in non-Latin scripts as long as the locale defines the rules for converting them. It should call the libc's towupper() on each successfully decoded character like zsh's $var:u Commented Aug 17, 2021 at 13:03
1

You can do it in the command line using GNU sed (what you are most likely using on your Linux system):

sed -e 's/\([a-z]\)/\U\1/g' filename >newfilename

Explanation:

sed is a program you can use to process a stream of strings into something else using regular expressions. It accepts input from standard input or from a file, and writes to standard output.

Now to break down the regular expression used:

to do a substitution, you use the syntax s/<expression>/<new_expression>/g. You use one expression to find the matching text you want to substitute and the other to dictate how to replace. The regular expression we used to find the match was ([a-z]) (ignoring the backslashes which escape the parentheses). This regular expression looks for any lower case character; surrounding the expression with parentheses lets us save the character for future reference.

Then, for the substitution, we use \U\1, where \U converts to upper case ("U" for upper), and \1 is the character we saved by surrounding the regex with parentheses


Further Reading:

3
  • More efficient to do the whole line at once: sed 's/.*/\U&/' file.
    – Kusalananda
    Commented Aug 17, 2021 at 11:43
  • 1
    @Kusalananda, note that it would only change the first sequence of characters on each line. Adding the g flag (as in my answer) would help for bogus text files that contain spurious bytes not forming part of valid characters. Also (re your edit), how do you determine which is the most common sed implementation on Linux. With 99.9% of Linux-based systems running some embedded OS or Android, I doubt a large portion would include GNU sed (if any sed), though I'll agree the OP's one will likely not be one of those. Commented Aug 17, 2021 at 11:51
  • @StéphaneChazelas You are correct on both accounts. In my comment, I was echoing the GNU sed manual without thinking.
    – Kusalananda
    Commented Aug 17, 2021 at 12:04
0

A perl one-liners are a fun way of doing this.

perl -ne 'print uc' NewFile.txt > output.txt

The -n assigns each line to $_ which is passed as a default argument to uc (uppercase) and is executed with -e. Or you could use

perl -pe '$_ = uc' NewFile.txt > output.txt

where the -p is the same as -n, but adds an implicit print $_ to the end of the -e command.

A big motto in Perl is There Is More Than One Way To Do It.

2
  • 1
    Note that perl -pe '$_=uc' (though with -Mopen=locale so as to work with text in the user's format instead of ASCII) was already mentioned in my answer. Commented Aug 17, 2021 at 9:11
  • @stéphane-chazelas The reason I missed your perl answer was that you started out with tr and awk and only half way down, you mentioned perl which just looked like more code while I was skimming responses. I'd suggest that when you consider different languages in your answer that you bold them or use a markdown header to organize your response for us tl;dr types ;)
    – Boyd
    Commented Aug 27, 2021 at 8:24
0

The suggested sed-oneliners (@Stéphane Chazelas, @Kusalananda) all use the "s"-subcommand, which is not the best tool for the job. Changing one (set of) character(s) to another is easier and better done with "y":

sed 'y/abcde/ABCDE/' infile > outfile

will translate any occurrence of the first character in the first string to the first character in the second string, any occurrence of the second character in the first string to the second character in the second string and so on. Replace in the above the "abcde"-strings with whatever "character translation table" you want and you can convert files with any character set/encoding sed is able to handle.

1
  • 1
    You can't change ß to SS or to FFI with that though (the s command with \U in GNU sed or vim, contrary to that of perl doesn't either though). Doing it for the ~2000 lower case characters in Unicode would end up in a several kilobyte large command line. Note that sed's y command works like tr's except it doesn't support ranges. tr abcde ABCDE which would be the same can be written tr a-e A-E. Commented Aug 18, 2021 at 5:51

You must log in to answer this question.

Not the answer you're looking for? Browse other questions tagged .