Opens in a new tab
Linux basics · Lesson 2 of 13

Bash quoting: how one filename became two arguments

In brief

The file is there, but ls says it is missing. Follow what Bash does to spaces, patterns and variables before the command receives them.

7 min read

BeginnerLessonAbout reading levels
Is this the right starting point?
Before you read
Ordinary computer use; the optional hands-on route reuses the disposable Ubuntu VM from lesson one. Commands, arguments, quoting and help are explained here.
What you will take away
Predict Bash argument formation and choose command-specific help, including when quotes suppress a desired filename expansion.
Sources
A large paper Enter key sits beside scattered colored shapes and three open paper boxes.
On this page8 sections

The directory contains weekly report.txt. You can see it. Yet this command complains that the file is missing:

ls -- weekly report.txt

The complaint names weekly and report.txt separately. You asked for one file; the command received two names. Nothing needs renaming yet. First, look at what happened to the line before the command saw it.

A small Ubuntu lab makes that journey visible: from the characters you type to the arguments a command receives. Quotes, patterns and variables can change those arguments, and the documentation explains which part is doing the changing.

Before ls runs, Bash reads the line

Your keystrokes arrive through a terminal, the interface that accepts input and displays text. Inside it, Bash, the shell used in this course, interprets the line. Then a command such as ls lists files or information about them.

Before that command runs, Bash may expand a variable, replace a filename pattern with matching names or arrange where output goes. Some commands are built into Bash; others are separate programs. Both receive the resulting arguments: values such as options and filenames.

The unquoted space in our first line separates two filename arguments. Quotes keep the name together:

ls -- 'weekly report.txt'

Bash uses the quotes as instructions, then removes them before passing the filename to ls. Soon we will create the file and compare the two lines on the same directory.

Bash passes weekly and report.txt to ls as separate operands without quotes. Quoting weekly report.txt keeps the spaced name in one operand.
An unquoted space separates two filename arguments. Quotes let Bash pass one filename containing that space.

A command, an option and a target

ls -l -- /etc/hosts

Here ls does the listing, and -l asks for the long form, including permissions, owner and size. /etc/hosts starts at the filesystem root: it is an absolute path, identifying the same file regardless of your current directory. That file commonly maps IP addresses to hostnames. This command lists its metadata, not its contents, and does not edit it.

For GNU ls, -- means the options have ended. A filename beginning with a hyphen after it is treated as a target, or operand, rather than another option. Many commands use this convention; their own documentation decides. The meaning of -l also belongs to ls, not to every program.

An unfamiliar line becomes easier to read when you separate the command, the requested behavior and the targets. You can look up those pieces without memorizing every combination.

Try both versions in the Ubuntu practice directory

Enter the disposable VM from lesson one. It provides consistent Bash and GNU utilities away from your host:

multipass shell linux-basics

If needed, run multipass start linux-basics on the host first. Everything that follows runs inside Ubuntu. Other shells or utility versions can behave differently. Reading along without running the commands is also fine.

Give the experiment its own directory and move into it:

mkdir -p "$HOME/aiopssre-lab/lesson-02"
cd -- "$HOME/aiopssre-lab/lesson-02"
pwd

mkdir -p creates missing parents and accepts a directory that already exists. cd changes location; pwd prints it. Check that the result ends in /aiopssre-lab/lesson-02. If either setup command failed, resolve that before continuing: the relative paths below depend on being here.

Make four synthetic practice files:

touch -- alpha.log beta.log notes.txt 'weekly report.txt'

touch creates a file if it is absent. For an existing file, it updates timestamps without emptying the contents. Keep unrelated files outside this directory so the results stay easy to follow.

ls -- weekly report.txt
ls -- 'weekly report.txt'

The first line searches for two missing names. The second finds the one name containing a space. The disk did not change between those commands. The arguments did.

The pattern expands before printf sees it

An unquoted * can match a sequence of filename characters. Put visible boundaries around the resulting names:

printf '<%s>\n' *.log

The quoted format uses %s for a string, angle brackets to surround it and \n for a new line. Bash’s printf reuses that format for remaining arguments. With only the two practice log files present, the result is:

<alpha.log>
<beta.log>

Bash found those filenames before printf ran. The printing command never searched the directory. Now put quotes around the pattern:

printf '<%s>\n' '*.log'
<*.log>

Single quotes keep the asterisk literal, leaving one string to print. Around the spaced filename, quotes kept the name we wanted intact. Around this pattern, they stopped an expansion we previously wanted. Quoting is a choice about what the command should receive.

Extra lines mean other .log files matched too. If nothing matches, ordinary Bash defaults leave the pattern unchanged; nullglob and failglob can alter that behavior. Seeing a pattern in a command is not proof a matching file exists.

Let a variable expand without splitting its value

The setup also used "$HOME". HOME normally holds your home-directory path. The dollar sign asks Bash to substitute its value.

printf '<%s>\n' '$HOME'
printf '<%s>\n' "$HOME"

Single quotes print <$HOME> literally. Double quotes allow the value to expand while keeping it in one argument. If the home directory is /home/ubuntu, the second line prints </home/ubuntu>.

Use the spaced filename again, this time as a variable’s value:

report='weekly report.txt'
printf '<%s>\n' $report
printf '<%s>\n' "$report"

With the lab’s normal Bash settings, the unquoted value becomes <weekly> and <report.txt> on separate lines. The double-quoted value stays <weekly report.txt>. Testing only notes.txt would have hidden the mistake. The space makes it visible.

The message went to a file instead

Ordinary command output usually goes to standard output, a stream the shell can connect to the terminal, a file or another command. Here > sends it into a practice file:

printf '%s\n' 'Practice run complete' > practice-note.txt

The terminal stays quiet because the message went into the file. Read it back:

cat -- practice-note.txt
Practice run complete

cat copies the contents to standard output, which this time reaches the terminal. In the earlier line, Bash opened the destination before starting printf. The filename was a destination, not another string to print. If Bash cannot open it, that command does not run.

This explains how a successful command can appear silent. It also explains the risk: > replaces existing contents. Keep this experiment on its dedicated practice file, never a document you need.

Find help for the command you are actually using

Before choosing documentation, find out what the name means in this session:

type ls
type printf

ls could name an external program or an alias that adds options. Bash normally supplies printf as a builtin; a familiar name could also resolve to a function. Read the result rather than assuming it points to a program on disk.

Ask Bash about its builtin:

help printf

Ask GNU ls for its help and manual:

ls --help
man ls

--help provides a quick summary. The manual opens in a pager. In the usual pager, type /--directory and Enter to search, n for the next match and q to leave. Minimal Ubuntu images may lack man or its pages; command help and the GNU manual remain alternatives.

In a synopsis, square brackets usually indicate optional parts, and an ellipsis indicates repetition. They describe the accepted input; they are not punctuation to copy into your command.

Find -d, also called --directory. It lists the directory itself instead of what is inside. What should this line show?

ls -ld -- "$HOME/aiopssre-lab/lesson-02"

Bash expands $HOME while keeping one path. GNU ls reads -ld as -l plus -d, so expect one metadata line for lesson-02. Compare it with:

ls -l -- "$HOME/aiopssre-lab/lesson-02"

Without -d, the listing describes the directory’s contents. -a is another documented option, including dot-prefixed names. Pick each option to answer a particular question rather than adding letters by habit.

The filename weekly report.txt was correct all along. The missing piece was how Bash divided the line before handing it to the command. You now have a way to inspect those arguments and find the right explanation when a command surprises you. Continue with Linux basics to put that foundation to work with files and directories.

Sources

These Bash and GNU references describe the behavior used in the examples:

Sources & context

Sources linked in this article. Read alongside the author’s analysis; a citation does not independently verify a publisher’s claims.

Report an error or outdated detail

Keep reading

Where to go next