0

I'd like to use "ed" to move lines up and down in linefeed-delimited text held in a variable. For example, to move line two down:.

input from variable:

one
two
three
four

output to variable:

one
three
two
four

The following one-liner works beautifully for a file, but I'd like to feed it a variable.

printf '%s\\n' '2,2m3' 'wq' | /bin/ed -s '/inputFile'

I've tried assorted variations on

printf '%s\\n' '2,2m0' 'wq' | /bin/ed -s <<<$inputVariable

which don't work, and my redirection expertise is clearly insufficient to the puzzle.

I'm running macOS 10.15.1.

3
  • Just curious, what is the output of echo $SHELL? I don't use Mac, but we did run into another question recently where MacOS was defaulting to zsh instead of bash which would explain why your herestring was not working with a variable. Commented Dec 7, 2019 at 3:47
  • The content of here-string is also written to ed's stdin, that might be the problem. I have no experience with ed though Commented Dec 7, 2019 at 4:34
  • macOS does indeed default to zsh, but my script is definitely #!/bash . Commented Dec 7, 2019 at 6:53

2 Answers 2

3

The ed program process commands from STDIN only (and does not support passing commands via the command line). To make this work, you need to represent the input as a file name (not necessary a file).

The following "convert" the variable input to a filename (/dev/fd/3) using "here string",

#! /bin/bash

input=$(ls -l)
out=$(printf '%s\n' '2,2m3' '1,$p' | ed 3<<<"$input" /dev/fd/3)
echo "$out"

Alternatively, with explicit reference to /dev/fd, or to specific file descriptor.

#! /bin/bash

input=$(ls -l)
out=$(ed <(printf '%s', "$input") <<___
2,2m3
1,\$p
___
Sign up to request clarification or add additional context in comments.

5 Comments

/proc isn't universal; I think /dev/fd/3 is more portable (though it's not universal either). In bash, you could use ed <(echo "$input").
@GordonDavisson as mentioned in answer, if STDIN is used for the input (variable), there is no way to pass the commands to ed.
@GordonDavisson On Linux, the '/dev/fd' is symlinked to /proc/self/fd'. If /proc/ not available, than no '/dev/fd'. Also process substitution is (usually) implemented (on Linux) with /dev/fd - same dependency. I've modified the code to use the /dev/fds,
That's just how Linux does it. OP mentioned that they're running macOS, which doesn't have /proc at all, but has /dev/fd/* (as direct character special devices, not symlinks). I checked a NetBSD system, and it's similar.
dash-o's out=$(printf '%s\n' '2,2m3' '1,$p' | ed 3<<<"$input" /dev/fd/3) works perfectly under Darwin with the addition of -s to suppress diagnostics. out=$(printf '%s\n' '2,2m3' '1,$p' | ed -s 3<<<"$input" /dev/fd/3) Thanks.
1

You can also use Bash's built-in index manipulation rather than spawning ed:

#!/usr/bin/env bash

inputVariable='one
two
three
four
five
six
'

IFS=$'\n' set -- $inputVariable

swp=3 # swap lines 3 and 4

printf '%s\n' "${@:1:swp-1}" "${@:swp+1:1}" "${@:swp:1}" "${@:swp+2}"

Output:

one
two
four
three
five
six

Detail of functions:

  • IFS=$'\n' set -- $inputVariable: Transfers $inputVariable's lines as arguments.

  • printf '%s\n': Prints arguments as lines.

  • "${@:1:swp-1}": Expands all arguments lines from 1st index to last index before the swap.

  • "${@:swp+1:1}" "${@:swp:1}": Performs the actual arguments lines swap, with next index swp+1 followed by swp index.

  • "${@:swp+2}": Expands all remaining arguments after the swapped indexes swp+2.

Comments

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.