I have some script name along with their path stored in a text file. The text files contains paths like


these shell files are being called by some main scripts

  • I want to go the path, the main path are present in the path(i dont know how to get the path, couldn't sed last delimiter)

  • I want to grep the filename(to get the file name use sed 's/.*\///' new.txt) and store the filename that is of type shell script into a text file.

The output in the text file should be something like path along with scriptname along with main script name

 /myhome/new1/myfiles/test2.sh  test2    test2main.sh
 /myhome/new3/myfiles/test1.sh  test1    test1foo.sh
 /myhome/new2/myfiles/test4.sh  test4    test4bar.sh
 /myhome/new/myfiles/test5.sh   test5    baz5main.sh

test2main.sh, test1foo.sh, testo4bar.sh ... are main scripts that call these files.

  • Help me understand that. You expect the command to read the file with those script paths. When it reads /myhome/new1/myfiles/test2.sh, it looks for script files in /myhome/new1/myfiles/ which call test2.sh, finds test2main and prints a line with the path, file name with stripped .sh and the script that has been found? – Philippos Sep 11 at 10:46
  • command will look for the script in the script path /myhome/new1/myfiles/ then search for the main scripts test2main or routines that are calling the script test2.sh....there may be other type of files that will call the script but only script file should be included in o/p. to find the main script(that is present in the same directory as the script) I grep the script name test2.sh in the path and bunch of files pop out ......but i only need the shellscript. – amar2108 Sep 11 at 11:51
  • @Philippos yes i think you have got it. – amar2108 Sep 11 at 11:55
  • Okay, but what is the criterion to know it is a shellscript? A certain shebang line? Being executable? Having .sh ending? – Philippos Sep 11 at 12:16
  • having .sh extension is fine. – amar2108 Sep 11 at 12:26

You can use the following script to achieve this. From your question, it is not clear how exactly you determine the "main" file for each input file. In the script below, it is simply assumed to be the only other file in the folder.

#!/usr/bin/env bash

while IFS= read -r f  # every line is represented by $f
    d=$(dirname "$f") # $f's directory
    b=$(basename "$f") # $f's basename
    bwoe=${b%.*} # $f's basename without extension
    m=$(find "$d" -mindepth 1 -maxdepth 1 -not -name "$b" | head -n 1) # get first file in the folder that is not $f
    m=$(basename "$m") # basename of main file
    printf "%s\\t%s\\t%s\\n" "$f" "$bwoe" "$m"
done < input.txt # input.txt contains the input scripts


$ ls a b c
afoo.sh  a.sh

bmain.sh  b.sh

c.sh  cxx.sh
$ cat input.txt 
$ bash script.sh 
a/a.sh  a       afoo.sh
b/b.sh  b       bmain.sh
c/c.sh  c       cxx.sh
  • actually the folder contains subscripts and only 1 main script for all subscript, so i needed to check for only the 1 main script calling the scripts given to me. – amar2108 Sep 17 at 5:59

If I understood you correctly (see comment), GNU sed's e flag to the s command can help you make it a one-liner (untested, sorry):

sed -E 'h;s_(.*/)(.*)_grep -l \2 \1*.sh_e;x;G;s_([^/]*).sh\n_\1.sh \1 _' scriptfile
  • h saves the line in the hold space, so we can recover it laater after messing it up
  • s_.*/_grep -l \2 \1*.sh_e is the actual trick: .*/ matches everything upto the last slash, so \1 will be the path, while \2 will be the rest of the line (the script name). Now grep -l is inserted before the script name and before the path; thus /my/path/foo.sh becomes grep -l foo.sh /my/path/*.sh. The e flag executes this pattern in a shell, so it gets replaced by the list of *.sh files in /my/path that contain foo.sh (hopefully only one, otherwise the script needs to be adapted)
  • The rest is easy: xchange both buffers and append the hold buffer (now with the shell's answer) to the pattern
  • s_([^/]*).sh\n_\1.sh \1 _ does the cleanup, duplicate the script name (without the .sh) and remove the newline, so you should get the desired output

The c-shell can be a real savior here:

% foreach line ( "`cat ./input.txt`" )
   printf '%s\t%s\t' ${line:q} ${line:r:t:q}
   printf '%smain\n' ${line:r:t:q}

Other methods are:

$ perl -F/ -pale '
   my($t) = $F[-1] =~ /([^.]+)/;
   $_ = join "\t", $_, $t, "${t}main" if /\S/;
' ./input.txt

$ sed -Ee '
' ./input.txt


/myhome/new1/myfiles/test2.sh   test2   test2main
/myhome/new3/myfiles/test1.sh   test1   test1main
/myhome/new2/myfiles/test4.sh   test4   test4main
/myhome/new/myfiles/test5.sh    test5   test5main


o We have to be verbose as we need to cover the various possible cases that may arise.
o GNU sed is needed.
  • According to the question, you can't rely on the script names to be test1main and so on, but it says to grep for the script name calling the other script. So your answer works for the example, but not for the real-world problem. – Philippos Sep 12 at 7:20

You can use capture groups in sed with extended regex (-r option) to grab the file name and replace the original string along with your scripts in the final output:

sed -r 's/(^.*\/)([^.]*)(.*$)/\1\2\3 \2 \2main/' test.txt 

(^.*\/) -- 1st Capture, the path 
([^.]*) -- 2nd Capture, the file up to the dot 
(.*$)   -- 3rd Capture, the file suffix 
  • According to the question, you can't rely on the script names to be test1main and so on, but it says to grep for the script name calling the other script. So your answer works for the example, but not for the real-world problem. – Philippos Sep 12 at 7:20
  • According to the test data it looks like that's what he's looking for. The question was not stated very clearly, the example data should probably reflect the potential results for what he wants more clearly. It looks like you've updated it. – A.Danischewski Sep 12 at 12:59

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service, privacy policy and cookie policy

Not the answer you're looking for? Browse other questions tagged or ask your own question.