Explain: {,} in cp or mv Bash Shell Commands

I often see commands as follows posted on blog or forums
cp /etc/httpd/httpd.{,.bakup}
OR
mv resume{z,}.doc
What is the purpose of {,} in Linux and Unix shell commands?

Tutorial details
DifficultyEasy (rss)
Root privilegesNo
RequirementsNone
Time2m
{} is nothing but brace expansion and usually used to generate combinations. From the GNU/bash man page:

ADVERTISEMENTS

{,} in cp or mv Bash Shell Commands

Brace expansion is a mechanism by which arbitrary strings may be generated. This mechanism is similar to filename expansion, but the file names generated need not exist. Patterns to be brace expanded take the form of an optional preamble, followed by either a series of comma-separated strings or a seqeunce expression between a pair of braces, followed by an optional postscript. The preamble is prefixed to each string contained within the braces, and the postscript is then appended to each resulting string, expanding left to right.

Open terminal and type the following command:

echo foo{1,2,3}.txt

Sample outputs:

foo1.txt foo2.txt foo3.txt

Try the following additonal examples to create arguments for commands and save typing time:

### I am using echo for demo purpose only ####
echo file.txt{,.bak}
echo file-{a..d}.txt
echo mkdir -p /apache-jail/{usr,bin,lib64,dev}
echo cp httpd.conf{,.backup}
echo mv delta.{txt,doc}
Linux and Unix {,} in cp or mv Bash Shell Commands

Examples

You can use brace expansion to copy file, rename/backup file, or create directories. In this traditional example, make a backup of a file named file1.txt to file1.txt.bak, type:

cp -v file1.txt file1.txt.bak

You can save time with brace expansion as follows when using the cp command:

cp -v file1.txt{,.bak}

Sample outputs:

file1.txt -> file1.txt.bak

More brace expansion examples

Try the following examples. Want to list all pdf and png files from Downloads and Pictures folders on Linux? Try:
ls -l ~/{Downloads,Pictures}/*.{pdf,png}
Brace expansions may be nested on Linux and Unix systems. For example:

echo a{1,2,3}b
echo a{1,2}{b,c}
echo 'Hi '{Wends,Marlena,Vivek,Raj}$', let\'s be friends!\n'
sudo chown www:data /wwwdata/{images/{old,new},lib/{how_to?.?*,old_html}}

Putting it all together

Let us use bash for loop to update systems using the apt command or yum command:

## update all CentOS/ RHEL 7.x boxes named ##
for server in aws-{prod,backup-prod}-{db,www}-0{1..4}
do
   ssh -t vivek@${server} sudo -- sh -c 'yum update'
done

See “sudo: Sorry, you must have a tty to run sudo Error on a Linux and Unix” and “How to run multiple commands in sudo under Linux or Unix” for more info.

Conclusion

You learned how to use braceexpansionto create arbitrary strings when using bash or sh including {,} in cp or mv Bash. It is similar topathname expansion, but the filenames generated need not exist.
See bash(1) command man page for more information.

🐧 Get the latest tutorials on SysAdmin, Linux/Unix, Open Source/DevOps topics:
CategoryList of Unix and Linux commands
File Managementcat
FirewallCentOS 8 OpenSUSE RHEL 8 Ubuntu 16.04 Ubuntu 18.04 Ubuntu 20.04
Network Utilitiesdig host ip nmap
OpenVPNCentOS 7 CentOS 8 Debian 10 Debian 8/9 Ubuntu 18.04 Ubuntu 20.04
Package Managerapk apt
Processes Managementbg chroot cron disown fg jobs killall kill pidof pstree pwdx time
Searchinggrep whereis which
User Informationgroups id lastcomm last lid/libuser-lid logname members users whoami who w
WireGuard VPNCentOS 8 Debian 10 Firewall Ubuntu 20.04

ADVERTISEMENTS
14 comments… add one
  • tolli Feb 19, 2014 @ 21:31

    “brace expansion only works with file names”
    This is not true. For example:
    $ echo a{b,c,d}
    ab ac ad

    The command “host -t a cyberciti.{biz,com}” expands to “host -t a cyberciti.biz cyberciti.com”. The problem is that the ‘host’ command only accepts one hostname argument. It use the last argument (“cyberciti.com”) as a DNS server to look up the hostname. (See the ‘host’ man page for more details) Since cyberciti.com is not a DNS server, it just hangs waiting for a response.

    • 🐧 Nix Craft Feb 21, 2014 @ 19:53

      The faq has been updated. I appreciate your feedback and post.

      • tolli Feb 21, 2014 @ 21:12

        Thanks!
        By the way, if you use xargs, you can use brace expansion with the ‘host’ command, like this:

        $ echo cyberciti.{biz,com} | xargs -n 1 host -t a
        cyberciti.biz has address 75.126.153.206
        cyberciti.com has address 184.168.221.1

  • Farzan Jameel Mufti Feb 20, 2014 @ 0:36

    I can elaborate these expansions with examples.

    echo {a,b}c prints ac bc notice the space in between
    echo xyz{1,2}X prints xyz1X xyz2X
    echo AB{a,b,c}.txt print ABa.txt ABb.txt ABc.txt
    echo {a,b} prints a b

    Now to your question:
    cp /etc/httpd/httpd.{,.bakup}

    So lets see expansion
    cp /etc/httpd/httpd. /etc/httpd/httpd..bacup

    In the above question, I think you need to get rid of the first dot, which doesn’t make sense.

    It should be
    cp /etc/httpd/httpd.conf{,.bkup}
    Then it expands to
    cp /etc/httpd/httpd.conf /etc/httpd.conf.bkup

    Second question:
    mv resume{z,}.doc
    mv resumez.doc resume.doc
    Changing the name from resumez.doc to resume.doc
    HTH

  • laike9m Feb 20, 2014 @ 13:28

    Didn’t know this!

  • Kevin Lausen Feb 20, 2014 @ 22:43

    I watched a youtube video recently on the power of the command line where he did a command like:

    mkdir -p {2013,2014,2015}:{Jan,Feb,March,April,Jun,Jul,Aug,Sep,Oct,Nov,Dec}:{Sunday,Monday,Tuesday,Wednesday,Thursday,Friday,Saturday}

    # for creating a backup tree, for monthly/daily backups for each year.

    • Farzan Jameel Mufti Feb 21, 2014 @ 12:40

      It is got to be a slash instead of a colon. Like

      mkdir -p {2013,2014,2015}/{Jan,Feb,March,April,Jun,Jul,Aug,Sep,Oct,Nov,Dec}/{Sunday,Monday,Tuesday,Wednesday,Thursday,Friday,Saturday}
      
      • T.Mini Feb 22, 2014 @ 9:05

        A bad idea. A better way to manage backup dirs in monthly, weekly, and daily format. Let file system keep track of dates.

        drwxr-xr-x 10 root root 4096 Feb 20 22:02 daily.0
        drwxr-xr-x 10 root root 4096 Feb 19 22:02 daily.1
        drwxr-xr-x 10 root root 4096 Feb 18 22:02 daily.2
        drwxr-xr-x 10 root root 4096 Feb 17 22:02 daily.3
        drwxr-xr-x 10 root root 4096 Feb 16 22:02 daily.4
        drwxr-xr-x 10 root root 4096 Feb 15 22:02 daily.5
        drwxr-xr-x 10 root root 4096 Feb 14 22:02 daily.6
        drwxr-xr-x 10 root root 4096 Feb 22 02:02 hourly.0
        drwxr-xr-x 10 root root 4096 Feb 22 00:02 hourly.1
        drwxr-xr-x 10 root root 4096 Feb 21 06:02 hourly.10
        drwxr-xr-x 10 root root 4096 Feb 21 04:02 hourly.11
        drwxr-xr-x 10 root root 4096 Feb 21 22:02 hourly.2
        drwxr-xr-x 10 root root 4096 Feb 21 20:02 hourly.3
        drwxr-xr-x 10 root root 4096 Feb 21 18:02 hourly.4
        drwxr-xr-x 10 root root 4096 Feb 21 16:02 hourly.5
        drwxr-xr-x 10 root root 4096 Feb 21 14:02 hourly.6
        drwxr-xr-x 10 root root 4096 Feb 21 12:02 hourly.7
        drwxr-xr-x 10 root root 4096 Feb 21 10:02 hourly.8
        drwxr-xr-x 10 root root 4096 Feb 21 08:02 hourly.9
        drwxr-xr-x 10 root root 4096 Jan  4 22:02 monthly.0
        drwxr-xr-x 10 root root 4096 Nov 30 22:02 monthly.1
        drwxr-xr-x 10 root root 4096 Jan 26  2013 monthly.10
        drwxr-xr-x 10 root root 4096 Dec 29  2012 monthly.11
        drwxr-xr-x 10 root root 4096 Nov  2 22:02 monthly.2
        drwxr-xr-x 10 root root 4096 Sep 28 22:02 monthly.3
        drwxr-xr-x 10 root root 4096 Aug  3  2013 monthly.4
        drwxr-xr-x 10 root root 4096 Jun 29  2013 monthly.5
        drwxr-xr-x 10 root root 4096 Jun  1  2013 monthly.6
        drwxr-xr-x 10 root root 4096 May  4  2013 monthly.7
        drwxr-xr-x 10 root root 4096 Mar 30  2013 monthly.8
        drwxr-xr-x 10 root root 4096 Mar  2  2013 monthly.9
        drwxr-xr-x 10 root root 4096 Feb  8 22:02 weekly.0
        drwxr-xr-x 10 root root 4096 Feb  1 22:02 weekly.1
        drwxr-xr-x 10 root root 4096 Jan 25 22:02 weekly.2
        drwxr-xr-x 10 root root 4096 Jan 18 22:02 weekly.3
        
        • Anthony Jun 20, 2014 @ 1:51

          T.Mini… Fist what Farzen is describing is a LONG term archiving structure (no deletions over VERY long periods of time). What you are describing is a rolling backup structure (only keep a minimal backup). They both have there uses.

          Second… if you are going to use numbers… add leading zeros so they sort correctly.

          Add leading zero if needed….
          number=$(printf “%02d” $number)

          Going back to Farzen’s post… If you are storing by date, best idea is to use the STANDARD ISO date format as it… sorts correctly!

          date +’%Y-%m-%d’
          2014-06-20

          This format is (and mentioned) sortable, and does not have day-month order confict you get between american dates and british dates. That is, is the date “08-04-2000”, the 4th of august (American) or the 8th of July (Britich/Australian). Better to avoid any posible conflict by using a sortable international standard!

          If you also need a time (to the second) say for logging data… add it like this…
          The formating makes it obvious it is a date and time, and not just some ‘sequence number’. Note %R output a 24 hour clock whcih avoids AM-PM conflicts (something I have had to sort and it isn’t easy to do)

          date +’%Y-%m-%d_%R:%S’
          2014-06-20_11:42:07

  • magesh Mar 5, 2014 @ 9:37

    nice post

  • safeer Mar 6, 2014 @ 14:16

    Was not aware of this..Nice

  • Andy Mar 23, 2014 @ 15:14

    This is a nice post. I will definitely make use of this at work.

  • Anthony Jun 20, 2014 @ 1:35

    brace expansion is also good for enumerated arguments (typically numbers)

    that is things like {1..5}

    First it is limited to Bash > version 3.2
    some versions of MacOSX did not have such an advanced bash installed!
    If that is the case you may be better of using the “seq” command or function.

    echo {5..15} # forward can be negative numebrs
    5 6 7 8 9 10 11 12 13 14 15

    echo {15..5} # reverse order
    15 14 13 12 11 10 9 8 7 6 5

    echo {5..15..2} # increment supported in BASH v4.0+
    5 7 9 11 13 15

    echo {a..d} # single letters (only single letters)
    a b c d

    echo {c..a}{A..C} # or letter sequence (multiple expand)
    cA cB cC bA bB bC aA aB aC

    echo {{1..5},{23..27}} # brace expandsions nest – two ranges of numbers
    1 2 3 4 5 23 24 25 26 27

    It can do simple formated numbers
    echo {0007..11}
    0007 0008 0009 0010 0011

    printf “__%d__” {1..5}; echo “” # printf auto-loops multiple args
    __1____2____3____4____5__

    ASIDE: printf loops over its arguments, if arguments were left after doing the percent subsubstitions!

    WARNING: you can not use variables in this syntax!

    This for example it will NOT work … {1..$limit}
    limit=5
    echo {1..$limit}

    Using eval substitution will work (painful though it is)…
    echo $(eval echo {1..${MAX_SEGMENTS}})

    You better of using a “for( ; ; )” loop!

    • naab Nov 21, 2014 @ 19:29

      You can use the seq command to expand properly using a variable.

      Example :
      $ limit=5; echo $(seq 1 $limit)
      1 2 3 4 5

Leave a Reply

Your email address will not be published.

Use HTML <pre>...</pre>, <code>...</code> and <kbd>...</kbd> for code samples.