Read paths on stdin and spawn a new interactive shell for each line












0















Say I have the results of a long-running process, like this:



$ find $HOME -perm 777


Now, I could get the results in a pager like this:



$ find $HOME -perm 777 | less


and then cd to the relevant directories in a different virtual terminal. But I'd rather have a script that opens a new interactive shell for each line of output, like this:



$ find $HOME -perm 777 | visit-paths.sh


This way I can e.g. inspect each file or directory, check the timestamp, decide whether I need to change the permissions or delete files, etc.



It's doable with a bash script that reads paths either from a file or from stdin, like so:



#! /usr/bin/env bash

set -e

OLD_DIR=''
while IFS='' read -u 10 -r line || test -n "$line"
do
if test -d "$line"
then
VISIT_DIR="$line"
elif test -f "$line"
then
VISIT_DIR="$(dirname "$line")"
else
printf "Error: path does not exist: '%s'n" "$line"
exit 1
fi
if test "$VISIT_DIR" != "$OLD_DIR"
then
( cd "$VISIT_DIR" && $SHELL -i </dev/tty )
OLD_DIR="$VISIT_DIR"
continue
else
# Same as last time, skip it.
continue
fi
done 10< "${*:-/dev/stdin}"


and I can break out of the loop early if necessary with Ctrl-C. However, I'd like to take advantage of Pythons built-in set to skip visiting paths I've already seen, so I wrote this script:



#! /usr/bin/env python3

import argparse
import logging
import os
import subprocess
import sys

if __name__ == '__main__':
parser = argparse.ArgumentParser(
description='Visit files from file or stdin.'
)
parser.add_argument(
'-v',
'--verbose',
help='More verbose logging',
dest="loglevel",
default=logging.WARNING,
action="store_const",
const=logging.INFO,
)
parser.add_argument(
'-d',
'--debug',
help='Enable debugging logs',
action="store_const",
dest="loglevel",
const=logging.DEBUG,
)
parser.add_argument(
'infile',
nargs='?',
type=argparse.FileType('r'),
default=sys.stdin,
help='Input file (or stdin)',
)
args = parser.parse_args()
logging.basicConfig(level=args.loglevel)
shell_bin = os.environ['SHELL']
logging.debug("SHELL = '{}'".format(shell_bin))
already_visited = set()
n_visits = 0
n_skipped = 0
for i, line in enumerate(args.infile):
visit_dir = None
candidate = line.rstrip()
logging.debug("candidate = '{}'".format(candidate))
if os.path.isdir(candidate):
visit_dir = candidate
elif os.path.isfile(candidate):
visit_dir = os.path.dirname(candidate)
else:
logging.warning("does not exist: '{}'".format(candidate))
n_skipped +=1
continue
if visit_dir is not None:
real_dir = os.path.realpath(visit_dir)
else:
# Should not happen.
logging.warning("could not determine directory for path: '{}'".format(candidate))
n_skipped +=1
continue
if visit_dir in already_visited:
logging.info("already visited: '{}'".format(visit_dir))
n_skipped +=1
continue
elif real_dir in already_visited:
logging.info("already visited: '{}' -> '{}'".format(visit_dir, real_dir))
n_skipped +=1
continue
if i != 0:
try :
response = input("#{}. Continue? (y/n) ".format(n_visits + 1))
except EOFError:
sys.stdout.write('n')
break
if response in ["n", "no"]:
break
logging.info("spawning '{}' in '{}'".format(shell_bin, visit_dir))
run_args = [shell_bin, "-i"]
subprocess.call(run_args, cwd=visit_dir, stdin=open('/dev/tty'))
already_visited.add(visit_dir)
already_visited.add(real_dir)
n_visits +=1

logging.info("# paths received: {}".format(i + 1))
logging.info("distinct directories visited: {}".format(n_visits))
logging.info("paths skipped: {}".format(n_skipped))


However, I'm having some issues with the replies to the Continue? (y/n) prompt being passed to the shell that is spawned, causing errors like y: command not found. Do I need to do something different with the stdin when using subprocess.call?



Alternatively, is there a standard tool that makes this Python script unnecessary that I just haven't heard of?









share



























    0















    Say I have the results of a long-running process, like this:



    $ find $HOME -perm 777


    Now, I could get the results in a pager like this:



    $ find $HOME -perm 777 | less


    and then cd to the relevant directories in a different virtual terminal. But I'd rather have a script that opens a new interactive shell for each line of output, like this:



    $ find $HOME -perm 777 | visit-paths.sh


    This way I can e.g. inspect each file or directory, check the timestamp, decide whether I need to change the permissions or delete files, etc.



    It's doable with a bash script that reads paths either from a file or from stdin, like so:



    #! /usr/bin/env bash

    set -e

    OLD_DIR=''
    while IFS='' read -u 10 -r line || test -n "$line"
    do
    if test -d "$line"
    then
    VISIT_DIR="$line"
    elif test -f "$line"
    then
    VISIT_DIR="$(dirname "$line")"
    else
    printf "Error: path does not exist: '%s'n" "$line"
    exit 1
    fi
    if test "$VISIT_DIR" != "$OLD_DIR"
    then
    ( cd "$VISIT_DIR" && $SHELL -i </dev/tty )
    OLD_DIR="$VISIT_DIR"
    continue
    else
    # Same as last time, skip it.
    continue
    fi
    done 10< "${*:-/dev/stdin}"


    and I can break out of the loop early if necessary with Ctrl-C. However, I'd like to take advantage of Pythons built-in set to skip visiting paths I've already seen, so I wrote this script:



    #! /usr/bin/env python3

    import argparse
    import logging
    import os
    import subprocess
    import sys

    if __name__ == '__main__':
    parser = argparse.ArgumentParser(
    description='Visit files from file or stdin.'
    )
    parser.add_argument(
    '-v',
    '--verbose',
    help='More verbose logging',
    dest="loglevel",
    default=logging.WARNING,
    action="store_const",
    const=logging.INFO,
    )
    parser.add_argument(
    '-d',
    '--debug',
    help='Enable debugging logs',
    action="store_const",
    dest="loglevel",
    const=logging.DEBUG,
    )
    parser.add_argument(
    'infile',
    nargs='?',
    type=argparse.FileType('r'),
    default=sys.stdin,
    help='Input file (or stdin)',
    )
    args = parser.parse_args()
    logging.basicConfig(level=args.loglevel)
    shell_bin = os.environ['SHELL']
    logging.debug("SHELL = '{}'".format(shell_bin))
    already_visited = set()
    n_visits = 0
    n_skipped = 0
    for i, line in enumerate(args.infile):
    visit_dir = None
    candidate = line.rstrip()
    logging.debug("candidate = '{}'".format(candidate))
    if os.path.isdir(candidate):
    visit_dir = candidate
    elif os.path.isfile(candidate):
    visit_dir = os.path.dirname(candidate)
    else:
    logging.warning("does not exist: '{}'".format(candidate))
    n_skipped +=1
    continue
    if visit_dir is not None:
    real_dir = os.path.realpath(visit_dir)
    else:
    # Should not happen.
    logging.warning("could not determine directory for path: '{}'".format(candidate))
    n_skipped +=1
    continue
    if visit_dir in already_visited:
    logging.info("already visited: '{}'".format(visit_dir))
    n_skipped +=1
    continue
    elif real_dir in already_visited:
    logging.info("already visited: '{}' -> '{}'".format(visit_dir, real_dir))
    n_skipped +=1
    continue
    if i != 0:
    try :
    response = input("#{}. Continue? (y/n) ".format(n_visits + 1))
    except EOFError:
    sys.stdout.write('n')
    break
    if response in ["n", "no"]:
    break
    logging.info("spawning '{}' in '{}'".format(shell_bin, visit_dir))
    run_args = [shell_bin, "-i"]
    subprocess.call(run_args, cwd=visit_dir, stdin=open('/dev/tty'))
    already_visited.add(visit_dir)
    already_visited.add(real_dir)
    n_visits +=1

    logging.info("# paths received: {}".format(i + 1))
    logging.info("distinct directories visited: {}".format(n_visits))
    logging.info("paths skipped: {}".format(n_skipped))


    However, I'm having some issues with the replies to the Continue? (y/n) prompt being passed to the shell that is spawned, causing errors like y: command not found. Do I need to do something different with the stdin when using subprocess.call?



    Alternatively, is there a standard tool that makes this Python script unnecessary that I just haven't heard of?









    share

























      0












      0








      0








      Say I have the results of a long-running process, like this:



      $ find $HOME -perm 777


      Now, I could get the results in a pager like this:



      $ find $HOME -perm 777 | less


      and then cd to the relevant directories in a different virtual terminal. But I'd rather have a script that opens a new interactive shell for each line of output, like this:



      $ find $HOME -perm 777 | visit-paths.sh


      This way I can e.g. inspect each file or directory, check the timestamp, decide whether I need to change the permissions or delete files, etc.



      It's doable with a bash script that reads paths either from a file or from stdin, like so:



      #! /usr/bin/env bash

      set -e

      OLD_DIR=''
      while IFS='' read -u 10 -r line || test -n "$line"
      do
      if test -d "$line"
      then
      VISIT_DIR="$line"
      elif test -f "$line"
      then
      VISIT_DIR="$(dirname "$line")"
      else
      printf "Error: path does not exist: '%s'n" "$line"
      exit 1
      fi
      if test "$VISIT_DIR" != "$OLD_DIR"
      then
      ( cd "$VISIT_DIR" && $SHELL -i </dev/tty )
      OLD_DIR="$VISIT_DIR"
      continue
      else
      # Same as last time, skip it.
      continue
      fi
      done 10< "${*:-/dev/stdin}"


      and I can break out of the loop early if necessary with Ctrl-C. However, I'd like to take advantage of Pythons built-in set to skip visiting paths I've already seen, so I wrote this script:



      #! /usr/bin/env python3

      import argparse
      import logging
      import os
      import subprocess
      import sys

      if __name__ == '__main__':
      parser = argparse.ArgumentParser(
      description='Visit files from file or stdin.'
      )
      parser.add_argument(
      '-v',
      '--verbose',
      help='More verbose logging',
      dest="loglevel",
      default=logging.WARNING,
      action="store_const",
      const=logging.INFO,
      )
      parser.add_argument(
      '-d',
      '--debug',
      help='Enable debugging logs',
      action="store_const",
      dest="loglevel",
      const=logging.DEBUG,
      )
      parser.add_argument(
      'infile',
      nargs='?',
      type=argparse.FileType('r'),
      default=sys.stdin,
      help='Input file (or stdin)',
      )
      args = parser.parse_args()
      logging.basicConfig(level=args.loglevel)
      shell_bin = os.environ['SHELL']
      logging.debug("SHELL = '{}'".format(shell_bin))
      already_visited = set()
      n_visits = 0
      n_skipped = 0
      for i, line in enumerate(args.infile):
      visit_dir = None
      candidate = line.rstrip()
      logging.debug("candidate = '{}'".format(candidate))
      if os.path.isdir(candidate):
      visit_dir = candidate
      elif os.path.isfile(candidate):
      visit_dir = os.path.dirname(candidate)
      else:
      logging.warning("does not exist: '{}'".format(candidate))
      n_skipped +=1
      continue
      if visit_dir is not None:
      real_dir = os.path.realpath(visit_dir)
      else:
      # Should not happen.
      logging.warning("could not determine directory for path: '{}'".format(candidate))
      n_skipped +=1
      continue
      if visit_dir in already_visited:
      logging.info("already visited: '{}'".format(visit_dir))
      n_skipped +=1
      continue
      elif real_dir in already_visited:
      logging.info("already visited: '{}' -> '{}'".format(visit_dir, real_dir))
      n_skipped +=1
      continue
      if i != 0:
      try :
      response = input("#{}. Continue? (y/n) ".format(n_visits + 1))
      except EOFError:
      sys.stdout.write('n')
      break
      if response in ["n", "no"]:
      break
      logging.info("spawning '{}' in '{}'".format(shell_bin, visit_dir))
      run_args = [shell_bin, "-i"]
      subprocess.call(run_args, cwd=visit_dir, stdin=open('/dev/tty'))
      already_visited.add(visit_dir)
      already_visited.add(real_dir)
      n_visits +=1

      logging.info("# paths received: {}".format(i + 1))
      logging.info("distinct directories visited: {}".format(n_visits))
      logging.info("paths skipped: {}".format(n_skipped))


      However, I'm having some issues with the replies to the Continue? (y/n) prompt being passed to the shell that is spawned, causing errors like y: command not found. Do I need to do something different with the stdin when using subprocess.call?



      Alternatively, is there a standard tool that makes this Python script unnecessary that I just haven't heard of?









      share














      Say I have the results of a long-running process, like this:



      $ find $HOME -perm 777


      Now, I could get the results in a pager like this:



      $ find $HOME -perm 777 | less


      and then cd to the relevant directories in a different virtual terminal. But I'd rather have a script that opens a new interactive shell for each line of output, like this:



      $ find $HOME -perm 777 | visit-paths.sh


      This way I can e.g. inspect each file or directory, check the timestamp, decide whether I need to change the permissions or delete files, etc.



      It's doable with a bash script that reads paths either from a file or from stdin, like so:



      #! /usr/bin/env bash

      set -e

      OLD_DIR=''
      while IFS='' read -u 10 -r line || test -n "$line"
      do
      if test -d "$line"
      then
      VISIT_DIR="$line"
      elif test -f "$line"
      then
      VISIT_DIR="$(dirname "$line")"
      else
      printf "Error: path does not exist: '%s'n" "$line"
      exit 1
      fi
      if test "$VISIT_DIR" != "$OLD_DIR"
      then
      ( cd "$VISIT_DIR" && $SHELL -i </dev/tty )
      OLD_DIR="$VISIT_DIR"
      continue
      else
      # Same as last time, skip it.
      continue
      fi
      done 10< "${*:-/dev/stdin}"


      and I can break out of the loop early if necessary with Ctrl-C. However, I'd like to take advantage of Pythons built-in set to skip visiting paths I've already seen, so I wrote this script:



      #! /usr/bin/env python3

      import argparse
      import logging
      import os
      import subprocess
      import sys

      if __name__ == '__main__':
      parser = argparse.ArgumentParser(
      description='Visit files from file or stdin.'
      )
      parser.add_argument(
      '-v',
      '--verbose',
      help='More verbose logging',
      dest="loglevel",
      default=logging.WARNING,
      action="store_const",
      const=logging.INFO,
      )
      parser.add_argument(
      '-d',
      '--debug',
      help='Enable debugging logs',
      action="store_const",
      dest="loglevel",
      const=logging.DEBUG,
      )
      parser.add_argument(
      'infile',
      nargs='?',
      type=argparse.FileType('r'),
      default=sys.stdin,
      help='Input file (or stdin)',
      )
      args = parser.parse_args()
      logging.basicConfig(level=args.loglevel)
      shell_bin = os.environ['SHELL']
      logging.debug("SHELL = '{}'".format(shell_bin))
      already_visited = set()
      n_visits = 0
      n_skipped = 0
      for i, line in enumerate(args.infile):
      visit_dir = None
      candidate = line.rstrip()
      logging.debug("candidate = '{}'".format(candidate))
      if os.path.isdir(candidate):
      visit_dir = candidate
      elif os.path.isfile(candidate):
      visit_dir = os.path.dirname(candidate)
      else:
      logging.warning("does not exist: '{}'".format(candidate))
      n_skipped +=1
      continue
      if visit_dir is not None:
      real_dir = os.path.realpath(visit_dir)
      else:
      # Should not happen.
      logging.warning("could not determine directory for path: '{}'".format(candidate))
      n_skipped +=1
      continue
      if visit_dir in already_visited:
      logging.info("already visited: '{}'".format(visit_dir))
      n_skipped +=1
      continue
      elif real_dir in already_visited:
      logging.info("already visited: '{}' -> '{}'".format(visit_dir, real_dir))
      n_skipped +=1
      continue
      if i != 0:
      try :
      response = input("#{}. Continue? (y/n) ".format(n_visits + 1))
      except EOFError:
      sys.stdout.write('n')
      break
      if response in ["n", "no"]:
      break
      logging.info("spawning '{}' in '{}'".format(shell_bin, visit_dir))
      run_args = [shell_bin, "-i"]
      subprocess.call(run_args, cwd=visit_dir, stdin=open('/dev/tty'))
      already_visited.add(visit_dir)
      already_visited.add(real_dir)
      n_visits +=1

      logging.info("# paths received: {}".format(i + 1))
      logging.info("distinct directories visited: {}".format(n_visits))
      logging.info("paths skipped: {}".format(n_skipped))


      However, I'm having some issues with the replies to the Continue? (y/n) prompt being passed to the shell that is spawned, causing errors like y: command not found. Do I need to do something different with the stdin when using subprocess.call?



      Alternatively, is there a standard tool that makes this Python script unnecessary that I just haven't heard of?







      shell python tty stdin





      share












      share










      share



      share










      asked 6 mins ago









      Nathaniel M. BeaverNathaniel M. Beaver

      190118




      190118






















          0






          active

          oldest

          votes












          Your Answer








          StackExchange.ready(function() {
          var channelOptions = {
          tags: "".split(" "),
          id: "106"
          };
          initTagRenderer("".split(" "), "".split(" "), channelOptions);

          StackExchange.using("externalEditor", function() {
          // Have to fire editor after snippets, if snippets enabled
          if (StackExchange.settings.snippets.snippetsEnabled) {
          StackExchange.using("snippets", function() {
          createEditor();
          });
          }
          else {
          createEditor();
          }
          });

          function createEditor() {
          StackExchange.prepareEditor({
          heartbeatType: 'answer',
          autoActivateHeartbeat: false,
          convertImagesToLinks: false,
          noModals: true,
          showLowRepImageUploadWarning: true,
          reputationToPostImages: null,
          bindNavPrevention: true,
          postfix: "",
          imageUploader: {
          brandingHtml: "Powered by u003ca class="icon-imgur-white" href="https://imgur.com/"u003eu003c/au003e",
          contentPolicyHtml: "User contributions licensed under u003ca href="https://creativecommons.org/licenses/by-sa/3.0/"u003ecc by-sa 3.0 with attribution requiredu003c/au003e u003ca href="https://stackoverflow.com/legal/content-policy"u003e(content policy)u003c/au003e",
          allowUrls: true
          },
          onDemand: true,
          discardSelector: ".discard-answer"
          ,immediatelyShowMarkdownHelp:true
          });


          }
          });














          draft saved

          draft discarded


















          StackExchange.ready(
          function () {
          StackExchange.openid.initPostLogin('.new-post-login', 'https%3a%2f%2funix.stackexchange.com%2fquestions%2f509559%2fread-paths-on-stdin-and-spawn-a-new-interactive-shell-for-each-line%23new-answer', 'question_page');
          }
          );

          Post as a guest















          Required, but never shown

























          0






          active

          oldest

          votes








          0






          active

          oldest

          votes









          active

          oldest

          votes






          active

          oldest

          votes
















          draft saved

          draft discarded




















































          Thanks for contributing an answer to Unix & Linux Stack Exchange!


          • Please be sure to answer the question. Provide details and share your research!

          But avoid



          • Asking for help, clarification, or responding to other answers.

          • Making statements based on opinion; back them up with references or personal experience.


          To learn more, see our tips on writing great answers.




          draft saved


          draft discarded














          StackExchange.ready(
          function () {
          StackExchange.openid.initPostLogin('.new-post-login', 'https%3a%2f%2funix.stackexchange.com%2fquestions%2f509559%2fread-paths-on-stdin-and-spawn-a-new-interactive-shell-for-each-line%23new-answer', 'question_page');
          }
          );

          Post as a guest















          Required, but never shown





















































          Required, but never shown














          Required, but never shown












          Required, but never shown







          Required, but never shown

































          Required, but never shown














          Required, but never shown












          Required, but never shown







          Required, but never shown







          Popular posts from this blog

          濃尾地震

          How to rewrite equation of hyperbola in standard form

          No ethernet ip address in my vocore2