0

I'm trying to write a simple Linux make command with bash scripting. here is what I have written so far:

#!/usr/bin/env bash

function make_cmd()
{
  read target colon sources
  for src in $sources; do
    if [ $src -nt $target ]; then
      while read cmd && [[ $(echo "$cmd" | grep "$(printf '\t')"*) ]]; do
        echo "executing $cmd";
        eval ${cmd#$(printf '\t')};
      done
      break;
    fi
  done
}

this is the format of input:

target.file : source.file
[tab]command

for example:

target.txt : source.txt
    ls
    cd

the script works well but it cannot find commands starting with tab. it always execute them. for example, the commands in this input is still executed.

target.txt : source.txt
ls
cd

how can I fix this?

2
  • The problem is the use of read, which splits on whitespace i.e. including tabs. Perhaps do IFS=' ' at the beginning to let only a space separate words. Commented Dec 5, 2019 at 8:06
  • 1
    See also: Understanding "IFS= read -r line" Commented Dec 5, 2019 at 8:32

1 Answer 1

1

The builtin read command splits words using the value of IFS, which by default contains a space, a tab and a newline. Hence when using read to get the input, the tab is removed.

Begin the function with:

IFS_SAVE="$IFS"
IFS=' '

Now only spaces will separate words. At the end of the function restore IFS to its original value:

IFS="$IFS_SAVE"

Note that you can use a literal tab if escaped by a backslash. Furthermore I would not use grep to match the tab, use builtins where possible as that's faster. My version of the function would be:

function make_cmd()
{
  SAVE_IFS="$IFS"
  IFS=' '
  read target colon sources
  for src in $sources; do
    if [ $src -nt $target ]; then
      while read cmd; do
        case "$cmd" in
          $'\t'*)  echo "executing $cmd"
                eval ${cmd# }
                ;;
          *)    ;;
        esac
      done
      break;
    fi
  done
  IFS="$SAVE_IFS"
}

The $'\t' substitutes a literal tab (thanks to Kusalananda for the tip).

Insert a literal tab after the # in the variable substitution. Using the printf might be more readable though.

6
  • I added the lines you said to my code, still doesn't work. your code does not execute the cmd no matter how it is entered. Commented Dec 5, 2019 at 8:59
  • 1
    If you run cat -t filename on your script, does it show the tabs in the code as ^I? E.g. ^I\^I*)^Iecho "executing $cmd". If not, your editor isn't inserting the tabs as-is. Commented Dec 5, 2019 at 9:50
  • yes your right. it doesn't insert tabs. I will fix this and test everything again Commented Dec 5, 2019 at 10:08
  • @FatemehKarimi You may also be able to use $'\t'*) in the case statement. Commented Dec 5, 2019 at 10:54
  • @Kusalananda thanks my dear friend. I replaced it with $'\t'*) and it solved my problem. Commented Dec 6, 2019 at 18:49

You must log in to answer this question.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.