Replacement substring in shell input - bash

I get the set of strings as input in terminal. I need to replace the ".awk" substring to ".sh" in each string using shell and then output modified string.
I wrote such script for doing this:
#!/bin/bash
while read line
do
result=${line/.awk/.sh}
echo $result
done
But it gives me an error: script-ch.sh: 6: script-ch.sh: Bad substitution.
How should I change this simple script to fix error?
UPD: ".awk" may be inside the string. For example: "file.awk.old".

If you are using Bash, then there is nothing wrong with your substitution. There is no reason to spawn an additional subshell and use a separate utility when bash substring replacement was tailor made to do that job:
$ fn="myfile.awk.old"; echo "$fn --> ${fn/.awk/.sh}"
myfile.awk.old --> myfile.sh.old
Note: if you are substituting .sh for .awk, then the . is unnecessary. A simple ${fn/awk/sh} will suffice.
I suspect you have some stray DOS character in your original script.

Not sure why it works for you and not for me.. might be the input you're giving it. It could have a space in it.
This should work:
#!/bin/bash
while read line
do
result=$(echo $line | sed 's/\.awk/\.sh/')
echo $result
done

If you run chmod +x script.sh and then run it with ./script.sh, or if you run it with bash script.sh, it should work fine.
Running it with sh script.sh will not work because the hashbang line will be ignored and the script will be interpreted by dash, which does not support that string substitution syntax.

Related

Struggling with string splitting

Learning shell script after years of Windows scripting. I have qBittorrent-nox running in a TrueNAS 12.2 jail. qBittorrent provides a feature to run a command after a torrent download completes. I want to call a shell script to run two chown commands on the folder that is passed as a parameter. The folder will have spaces and may have ampersands, and the shell script fails as a result. The command passed according to qBittorrent's log file is this:
bash /mnt/torrents/Live/fixperms.sh "Name of the folder & description of contents"
This is the code I expected to work:
#!/usr/local/bin/bash
chown -R 1001:1006 $(printf "%q" "$1")
The command is correctly formed, but the script fails as it splits the string. The command it creates works if I echo it and execute it manually. I cannot find a way of making any shell (csh, sh, zsh and bash) NOT split the string at the spaces. I've tried it with single quotes, double quotes and backticks.
I have spent a number of hours on this and I have made no progress. I have tried all four shells, zsh splits the double-quoted string when the documentation says it shouldn't, parameter expansion works in bash but still splits the string. I have no python or perl, and no wish to install them if I can avoid it.
What am I missing?
Bash is 5.0.18, zsh is v5.8, tcsh is 6.2.00
You could do the following
VAR="Name of the folder & description of contents"; bash /mnt/torrents/Live/fixperms.sh "$VAR"
Then the string is handled with the blank spaces in it. If you for example to this in a bash, it works:
user#server:~$ VAR="test is test"
user#server:~$ ./test2.sh "$VAR"
test is test
test2.sh contains just an echo $1 so you are able to work with the entire string afterwards like
#!/usr/local/bin/bash
chown -R 1001:1006 $(printf "%q" "$1")

bash works alone but fails when running in screen

I have this bash script that starts with
for d in /data/mydata/*; do
echo $d
filepath=$(echo $d | tr "/" "\n")
pathArr=($filepath) # fails here
echo ${pathArr[-1]}
It runs fine when I just call in on command line
./run_preprocess.sh
but when I run it using screen
screen -dmSL run_preproc ./run_preprocess.sh
it fails on that pathArr line
./run_preproc.sh: 7: ./run_preproc.sh: Syntax error: "(" unexpected (expecting "done")
is there something I need to do to protect the script code?
Based on the error, looks like you're running your script with POSIX sh, not bash. Arrays are undefined in POSIX sh.
To fix this, add a proper hashbang to your script (e.g. /usr/bin/env bash, or run the script directly with Bash interpreter (e.g. /bin/bash script.sh).
In addition (unrelated to the problem at hand), your script (or the snippet posted) has several potential issues:
variables should be quoted to prevent globbing and word splitting (e.g. consider d - one of your files - containing * -- echo $d will include a list of all files, since * will be expanded)
splitting into array with ($var) is done on any IFS character, not just newlines. IFS includes a space, tab and newline by default. Use of read -a or mapfile is recommended over ($var).
Finally, if all you're trying is get the last component in path (filename), you should consider using basename(1):
$ basename /path/to/file
file
or substring removal syntax of Bash parameter expansion:
$ path=/path/to/file
$ echo "${path##*/}"
file

Terminal program: unable to find the directory/file though they are in the correct pathway

What else could be going wrong? Sorry I'm pretty new to programming so I'm not sure if this is the proper way to frame my question.
Here is the code from the terminal file:
echo "Patcher Coded by _Retro_"
PLACE=`dirname $0`
ROM=`ls ${PLACE}/Rom/*.nds | head -n 1`
PATCH=`ls ${PLACE}/Patch/*.* | head -n 1`
NAME=${ROM%.[^.]*}
$PLACE/xdelta3 -dfs $ROM $PATCH $NAME-patched.nds
Your script says this:
PLACE=`dirname $0`
First, the shell performs parameter expansion. That means (in this case) it expands $0. The variable $0 expands to the path used by the shell to execute your script, so that line expands to this:
PLACE=`dirname /Users/ShakeyBanks/Desktop/Perfect Heart CE./DS_Rom_Patcher/Rom_Patcher`
Note that there are no backslashes in the expansion! The backslashes were consumed by your interactive shell before starting the script.
Then the shell performs command substitution: it executes the command enclosed in `...`. The shell splits the command on spaces, so the command contains four words. The first word is the program to run, and the remaining three words are arguments to that command:
dirname
/Users/ShakeyBanks/Desktop/Perfect
Heart
CE./DS_Rom_Patcher/Rom_Patcher
The problem here is that the dirname program only wants one argument, but you're passing it three. It detects this and fails with an error:
usage: dirname path
To fix this, quote the $0 with double-quotes, like this:
PLACE=`dirname "$0"`
You also need to quote all subsequent uses of $PLACE, ${PLACE}, $ROM, $PATCH, and $NAME with double-quotes, because they will have the same problem.
OR, rename your directory to not contain spaces.

Using grep inside shell script gives file not found error

I cannot believe I've spent 1.5 hours on something as trivial as this. I'm writing a very simple shell script which greps a file, stores the output in a variable, and echos the variable to STDOUT.
I have checked the grep command with the regex on the command line, and it works fine. But for some reason, the grep command doesn't work inside the shell script.
Here is the shell script I wrote up:
#!/bin/bash
tt=grep 'test' $1
echo $tt
I ran this with the following command: ./myScript.sh testingFile. It just prints an empty line.
I have already used chmod and made the script executable.
I have checked that the PATH variable has /bin in it.
Verified that echo $SHELL gives /bin/bash
In my desperation, I have tried all combinations of:
tt=grep 'test' "$1"
echo ${tt}
Not using the command line argument at all, and hardcoding the name of the file tt=grep 'test' testingFile
I found this: grep fails inside bash script but works on command line, and even used dos2unix to remove any possible carriage returns.
Also, when I try to use any of the grep options, like: tt=grep -oE 'test' testingFile, I get an error saying: ./out.sh: line 3: -oE: command not found.
This is crazy.
You need to use command substitution:
#!/usr/bin/env bash
test=$(grep 'foo' "$1")
echo "$test"
Command substitution allows the output of a command to replace the command itself. Command substitution occurs when a command is enclosed like this:
$(command)
or like this using backticks:
`command`
Bash performs the expansion by executing COMMAND and replacing the command substitution with the standard output of the command, with any trailing newlines deleted. Embedded newlines are not deleted, but they may be removed during word splitting.
The $() version is usually preferred because it allows nesting:
$(command $(command))
For more information read the command substitution section in man bash.

How display color message from bash script?

I found this example:
echo -e "This is red->\e[00;31mRED\e[00m"
It works if execute direct, from command line, bu if create file like:
#! /usr/bin/sh
echo -e "This is red->\e[00;31mRED\e[00m"
Doesn't work. How to fix? Or may be possible output in bold?
Please don't use Lua it doesn't installed.
Edit This might be your problem (likely):
#!/bin/bash
echo -e "This is red->\e[00;31mRED\e[00m"
The reason is that sh doesn't have a builtin echo command, that supports escapes.
Alternatively you might invoke your script like
bash ./myscript.sh
Backgrounders
ANSI escape sequences are interpreted by the terminal.
If you run in a pipe/with IO redirected, ouput won't be to a terminal, hence the escapes don't get interpreted.
Hints:
see ansifilter for a tool that can filter ANSI escape sequences (and optionally translate to HTML and others)
use GNU less, e.g. to get ANSI escapes working in a pager:
grep something --colour=always files.* | less -R
Or simply, as I do
# also prevent wrapping long lines
alias less='less -SR'
Use an echo program, not an echo built-in command:
#!/bin/sh
MYECHO="`which echo`"
if <test-whether-MYECHO-empty-and-act-accordingly> ...
...
$MYCHO -e "This is red->\e[00;31mRED\e[00m"

Resources