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.
5 Answers
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 ffi
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
: changetoupper
totolower
dd
: changeucase
tolcase
- GNU
sed
/ex
/vi
: change\U
to\L
perl
: changeuc
tolc
.uconv
: changeupper
tolower
vim
: changegUG
toguG
(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.
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
-
1Also 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 asread
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 thanread
or$(<file)
). Commented Aug 17, 2021 at 12:50 -
Huh, somehow I never noticed that
$( )
dropped trailing newlines. Thanks for the heads up.– bxmCommented 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'stowupper()
on each successfully decoded character like zsh's$var:u
Commented Aug 17, 2021 at 13:03
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:
- Back-references and Subexpressions. These are the parentheses we used to save the match for future reference and referred to it again with
\1
- Upper/Lower case conversion. Here you will find a detailed description of
\U
, as well as other operators you can use for conversion
-
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 GNUsed
(if anysed
), 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
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.
-
1Note 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
andawk
and only half way down, you mentionedperl
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 ;)– BoydCommented Aug 27, 2021 at 8:24
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.
-
1You can't change
ß
toSS
orffi
toFFI
with that though (thes
command with\U
in GNUsed
orvim
, contrary to that ofperl
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 thatsed
'sy
command works liketr
's except it doesn't support ranges.tr abcde ABCDE
which would be the same can be writtentr a-e A-E
. Commented Aug 18, 2021 at 5:51