1

Someday I will understand bash var types. This isn't the day yet. I have the following code:

#!/bin/bash
FILES=( "monitor_api*" )
FILE="./"${FILES[0]}

echo $FILE
TFILE="./monitor_api_0.3a.tar.gz"
echo ${FILE:0:$(expr ${#FILE} - 3)}
echo ${TFILE:0:$(expr ${#TFILE} - 3)}

result:

./monitor_api_0.3a.tar.gz
./monitor_a
./monitor_api_0.3a.tar
  • Why is a concatenation with an array element different than a simple string ?
  • How can I make $FILE a string so I can remove the last 3 chars ?
  • Is there a better way to remove .gz from $FILE var ?

I just want to remove .gz from the filename.

Edit

@DavidC.Rankin and @CharlesDuffy

The objective of this script is getting the first file in the dir that starts with monitor_api add a "./" prefix and remove .gz suffix.

I've tried {FILE:0: -3}, ${FILE:0:(-3) and ${FILE%.gz} and none worked. Also notice I want to concatenate "./" before the the filename. Here's the result when using them:

#!/bin/bash 
FILES=( "monitor_api*" ) 
FILE=${FILES[0]} 
SFILE="./"$FILE 
 
echo $SFILE 
echo ${SFILE:0:-3} 
echo ${SFILE:0:(-3)} 
echo ${SFILE%.gz} 

result:

./monitor_api_0.3a.tar.gz
./monitor_a
./monitor_a
./monitor_api_0.3a.tar.gz
halfer
  • 19,824
  • 17
  • 99
  • 186
Nelson Teixeira
  • 6,297
  • 5
  • 36
  • 73
  • 1
    You seem to be mixing apples/oranges. `FILES` holds `"monitor_api*"` where normal glob expansion is prevented by quoting. `FILE` does the opposite and uses `monitor_api*` unquoted which will undergo expansion to names matching that prefix in the current working directory. Then you switch to another fruit salad and mix BASH parameter expansions `${file:0:...` with POSIX `expr` to attempt to get `length - 3`. where the correct BASH expansion would be `${FILE:0: -3}` (note the space or parenthesize it, e.g. `${FILE:0:(-3)}`). The issue is `FILE` is determined by what is in the local directory. – David C. Rankin Mar 07 '23 at 23:23
  • 1
    To remove `".gz"` from the end of `FILE` it is simply `"${FILE%.gz}"` (which works in both POSIX shell and bash) See [BASH - trim string parameter expansion - answer](https://stackoverflow.com/a/63147473/3422102) – David C. Rankin Mar 07 '23 at 23:25
  • You can always do `FILES=( monitor_api* )` (no quotes - pathname expansion occurs - but spaces in failenames are NOT handled). `${FILES[0]}` is the first element of the array corresponding to the first matching file. (or you can just use `${FILES}` to obtain the first element also. so `file_I_want="./${FILES%.gz}"` will give you `"./monitor_api_0.3a.tar"` if the first file was `monitor_api_0.3a.tar.gz` (as will the full form `"./${FILES[0]%.gz}"`) – David C. Rankin Mar 07 '23 at 23:45
  • 1
    A better way would be `for name in monitor_api*; do file_I_want="${name%.gz}"; break; done` to handle the possibility of spaces in filenames (you set up a loop and just take the first file) You can add a test, e.g. `[[ ${FILES[0]} =~ [.]gz$ ]]` to test if the file is a `.gz` file before taking it is the one you want. – David C. Rankin Mar 07 '23 at 23:48
  • 1
    When you say you tried the things you were advised and they didn't work, show us _how_ you tried them and _how_ they didn't work in enough detail that we can tell what went wrong. The advice you're being given is sound, so if things don't work, there's something going on in the details -- maybe you're running the code with `sh` instead of bash, maybe some prior command generated a string instead of an array, etc; but we can't tell what went wrong if the only thing you say is that it "didn't work" without enough details to see the problem ourselves. – Charles Duffy Mar 07 '23 at 23:51
  • For a working example showing the advice David gave you being applied correctly and functioning as intended, see https://replit.com/@CharlesDuffy2/UnwieldyDarkServerapplication – Charles Duffy Mar 07 '23 at 23:57
  • (btw, notice the use of a lowercase variable name in that example -- all-caps names are used for variables meaningful to the shell and operating system, whereas names with at least one lower-case variable are reserved for application use and guaranteed not to change the behavior of POSIX-specified tools; see https://pubs.opengroup.org/onlinepubs/9699919799/basedefs/V1_chap08.html, keeping in mind that setting a shell variable overwrites any like-named environment variable, so the same conventions necessarily apply to both). – Charles Duffy Mar 07 '23 at 23:59
  • (as another aside, `expr` is basically a code smell; it was defined back in the 1970s when `$(( ))` didn't exist yet, and is _extremely_ inefficient compared to shell-builtin math syntax that's been standardized since POSIX.2 came out in the early 90s) – Charles Duffy Mar 08 '23 at 00:00
  • [Shellcheck](https://www.shellcheck.net/) identifies several problems with the code. The report includes links to more information about the problems and how to fix them. It's a good idea to run [Shellcheck](https://www.shellcheck.net/) on all new and modified shell code. – pjh Mar 08 '23 at 00:54
  • @CharlesDuffy If you read the now history of the question, you'll see that when I said the instructions didn't work, I also said that in that moment I had to leave and would complement with the results when I return. I edited the question with the non-working results. But anyway, your comments made me find what was the problem: The quotes in FILES=( "monitor_api*" ). As soon as I removed them it all worked. – Nelson Teixeira Mar 08 '23 at 02:23
  • Now you guys gone to great lengths to help me. Don't you want put those comments in answer form so people can upvote ? If you decide not to, I will add my own answer so that readers know exactly what was wrong in my code. – Nelson Teixeira Mar 08 '23 at 02:24
  • As no one commented I will create an answer. If you decide to create any answer I'll delete my own later. – Nelson Teixeira Mar 08 '23 at 16:18
  • 1
    Insofar as the problem was caused by the wildcard being in quotes, the question is arguably a duplicate -- arguably, f/e, of [In bash, how do I expand a wildcard while it's inside double quotes?](https://stackoverflow.com/questions/28566599/in-bash-how-do-i-expand-a-wildcard-while-its-inside-double-quotes). I'm hesitant to close it, though, since pjh has assembled such an excellent answer. – Charles Duffy Mar 08 '23 at 18:09

2 Answers2

1

Try this Shellcheck-clean code:

#! /bin/bash -p

shopt -s nullglob

files=( monitor_api* )

if (( ${#files[*]} == 0 )); then
    printf "%s: ERROR: There are no 'monitor_api' files\\n" "$0" >&2
    exit 1
fi

file=./${files[0]%.gz}

printf '%s\n' "$file"

Answers to your questions:

  1. Why is a concatenation with an array element different than a simple string?

    It is not different.

  2. How can I make $FILE a string so I can remove the last 3 chars?

    $FILE is a string. It has the value (literal) ./monitor_api*. echo $FILE shows something different because it is subject to shell "pathname expansion" and the * gets expanded. Using echo "$FILE" would avoid the problem, but echo "$var" doesn't work in general. Only use echo for literal strings (if at all).

  3. Is there a better way to remove .gz from $FILE var ?

    The best option is ${FILE%.gz}. That is supported by all but the most ancient (i.e. 1980s and before) Bourne-derived shells.

pjh
  • 6,388
  • 2
  • 16
  • 17
1

The answer and comments already shown several problems. But just to keep the record, the blocking problem in my original code was the quotes in this line:

FILES=( "monitor_api*" )

as soon as I changed it to

FILES=( monitor_api* )

all the other commands worked fine

Nelson Teixeira
  • 6,297
  • 5
  • 36
  • 73
  • Note that there are other ways (apart from using an array in this way) to do what you want. See [Get first file of given extension from a folder](https://stackoverflow.com/q/23423764/4154375). (Ignore claimed solutions that use `ls` though. See [Why you shouldn't parse the output of ls(1)](http://mywiki.wooledge.org/ParsingLs).) – pjh Mar 08 '23 at 18:24