0

I want to get the filename from a file path. It works fine when one file is specified, such as:

fname=$(basename -- /tmp/file1)

However, if provided as an expression such as fname=$(basename -- /tmp/fi*) it throws basename: extra operand ‘/tmp/file1’.

In above case I don't expect it to resolve and expand the expression but rather just return fi* in fname, is it possible to do it using basename?

Maven
  • 14,587
  • 42
  • 113
  • 174
  • 2
    Look at the man-page for `basename`: You have to specify one single filename. There is a second operand allowed, which is a _suffix_. However, your wildcard expression likely expands to more than 2 strings. If you want to inhibit filename expansion, you have to quote the argument, or at least escape the wildcards: `basename /tmp/fi\*` should do. – user1934428 Jul 26 '23 at 12:55
  • `basename` never sees the pattern; it sees the *result* of the shell expanding the pattern. (If the pattern doesn't match anything, `basename` receives the string as-is by default, but regardless, `basename` doesn't expand the glob itself.) – chepner Jul 26 '23 at 15:18

3 Answers3

1

If you do not want to expand the expression add quotes around it.

fname=$(basename -- "/tmp/fi*")
Paul Pazderski
  • 473
  • 2
  • 8
  • I think OP meant, when the glob is not able to expand, need to return the string as-is. This will not work, when the glob is valid – Inian Jul 26 '23 at 12:49
  • @Inian : I don't think so. The example given by the OP shows that the glob is valid, i.e. that it **does** expand. – user1934428 Jul 26 '23 at 12:56
  • I still understand it the way I answered. But if someone really want to get the first match or the pattern this can work: `glob=(/tmp/fi*); fnname=$(basename -- "${glob[0]}")` – Paul Pazderski Jul 26 '23 at 12:57
1

As stated in comments; the basename command only provides base name for an individual file. If you need to collect base name from multiple files matching a wildcard * pattern, then you need to either work with arrays, or iterate the globbing pattern matches:

With Bash arrays:

#!/usr/bin/env bash

# Populates the files array with the wildcard pattern glob expansion
files=(/tmp/fi*)

# Strip each individual file names starting with anything and up to last /
# stores the results into another basenames array
basenames=("${files[@]##*/}")

# Iterate the basenames array to work with
for abasename in "${basenames[@]}"; do
  : do something with each "$abasename"
done

Now if the shell you use has no support for arrays, you may iterate directly and process base names one by one:

#!/bin/sh

for afile in /tmp/fi*; do

  # strip the directory path to get basename
  abasename="${afile##*/}"

  : do something with that "$abasename"
done
Léa Gris
  • 17,497
  • 4
  • 32
  • 41
0

temporarily disable globbing

fname=$(set -o noglob; basename /tmp/fi*)

note fname will contain the string "fi*"

M S
  • 137
  • 8
  • 2
    This enables file glob in the script even if it was previously disabled. If you want to do it with shell options you should place it in the command substitution which is a subshell and therefore does not influence the remaining script. `fname=$(set -o noglob; basename /tmp/fi*)` – Paul Pazderski Jul 26 '23 at 13:22
  • However, there's no reason to take that path, if you don't want wildcard expansion just quote or escape (in this precise case) – Diego Torres Milano Jul 26 '23 at 15:21