diff --git a/.gitignore b/.gitignore new file mode 100644 index 00000000..1a58a1f0 --- /dev/null +++ b/.gitignore @@ -0,0 +1,2 @@ +node_modules +myfile diff --git a/.jshintrc b/.jshintrc new file mode 100644 index 00000000..fc024bea --- /dev/null +++ b/.jshintrc @@ -0,0 +1,3 @@ +{ + "esversion": 6 +} diff --git a/.travis.yml b/.travis.yml new file mode 100644 index 00000000..bc6024b8 --- /dev/null +++ b/.travis.yml @@ -0,0 +1,6 @@ +language: node_js +node_js: + - "node" + - "6" + + diff --git a/README.md b/README.md index bc8b128c..364f3858 100644 --- a/README.md +++ b/README.md @@ -1,2 +1,74 @@ -# rexreplace -CLI regexp replacement with lookahead and backreference to matching groups in the replacement. What "sed" should have been. +[![Build Status](https://travis-ci.org/mathiasrw/rexreplace.svg?branch=master)](https://travis-ci.org/mathiasrw/rexreplace) +[![npm version](https://badge.fury.io/js/rexreplace.svg)](https://www.npmjs.com/package/rexreplace) + +# RexReplace + +CLI Regexp search and replace for files using lookahead and +backreference to matching groups in the replacement. Defaults to global +multiline case-insensitive search. Needs Node v6 or higher. + +### Install +```bash +> npm install -g rexreplace +``` + + + +### Usages +```bash +> rexreplace searchFor replaceWith filenameA filenameB filenameC ... +``` + +### Examples +```bash +> rexreplace Foo xxx myfile.md + # 'foobar' in myfile.md will become 'xxxbar' + +> rexreplace Foo xxx myfile.md -I + # 'foobar' in myfile.md will remain 'foobar' + +> rexreplace '(f?(o))o(.*)' '$3$1$2' myfile.md + # 'foobar' in myfile.md will become 'barfoo' + + # Some commandline tools (bash/zsh/...) can be a bit funny with the `$` sign. Use the '-€' flag to let `€` alias a `$` in pattern and replacement +> rexreplace '(f?(o))o(.*)' '€3€1€2' myfile.md -€ + # 'foobar' in myfile.md will become 'barfoo' + + + +``` + + +### Options + +``` + -v, --version Echo rexreplace version [boolean] + -I, --void-ignore-case Void case insensitive search pattern. [boolean] + -M, --void-multiline Void multiline search pattern. Makes ^ and $ match + start/end of whole content rather than each line. + [boolean] + -u, --unicode Treat pattern as a sequence of unicode code points. + [boolean] + -e, --encoding Encoding of files. [default: "utf8"] + -o, --output Output the result instead of saving to file. Will also + output content even if no replacement have taken + place. [boolean] + -q, --quiet Only display erros (no other info) [boolean] + -Q, --quiet-total Never display erros or info [boolean] + -H, --halt Halt on first error [boolean] [default: false] + -d, --debug Print debug info [boolean] + -€, --eurodollar Replace all '€' with '$' in pattern and replace + string. Usefull when your commandline (bash/zsh/...) + seeks to do interesting things with '$' [boolean] + -h, --help Show help [boolean] +``` + +### Future +- Handle globs +- Handle piped data + +### inspiration + +.oO(_What "sed" should have been by now_) + +The replace-in-file npm package (but with better naming and backreferences) diff --git a/aserta.sh b/aserta.sh new file mode 100644 index 00000000..bf0b0066 --- /dev/null +++ b/aserta.sh @@ -0,0 +1,548 @@ +#!/usr/bin/env bash +# +# name : aserta +# description : a handy unit testing framework +# repository : http://github.com/andamira/aserta +# author : José Luis Cruz © 2016-2017 +# Robert Lehmann © 2009-2015 (assert.sh) +# license : MIT & LGPLv3 +_ASERTA_VERSION=2.2.1 + + +#------------------------------------------------------------------------------- +# main +# +# shellcheck disable=SC2155 +main() { + + export DISCOVERONLY="${DISCOVERONLY:-}" + export DEBUG="${DEBUG:-}" + export STOP="${STOP:-}" + export INVARIANT="${INVARIANT:-}" + export CONTINUE="${CONTINUE:-}" + + # Commands substitutions + export __GREP=$(__gnuCmd grep) + export __DATE=$(__gnuCmd date) + + _assert_reset + : "${tests_suite_status:=0}" # remember if any of the tests failed so far + : "${tests_ran_total:=0}" # remember total number of tests (inc. failures) + : "${tests_failed_total:=0}" # remember the total number of test failures + + trap _assert_cleanup EXIT + + _indent=$'\n\t' # local format helper + + parseArguments "$@" + +} # main() + + +#------------------------------------------------------------------------------- +# parseArguments +# +# Parses the arguments received by the script and sets the pertinent options. +# +# Arguments: +# $@ - all the arguments passed to the script +# +parseArguments() { + local OPTIND option function + local optspec=":cdivxh-:" + + # Show usage and exit if: + # - the script has not been sourced + # - no arguments has been passed + [[ "$0" == "${BASH_SOURCE[0]}" && -z "$@" ]] && usage && exit 0 + + # Parse the options + + while getopts "$optspec" option; do + case "$option" in + + # long options + # http://stackoverflow.com/a/7680682/940200 + + -) + case "$OPTARG" in + continue) CONTINUE=1 ;; + discover) DISCOVERONLY=1 ;; + invariant) INVARIANT=1 ;; + verbose) DEBUG=1 ;; + stop) STOP=1 ;; + + help) usage ; exit 0 ;; + version) version; exit 0 ;; + + *) # error + if [[ "$OPTERR" == 1 ]]; then + printf 'Unknown option --%s\n' "$OPTARG" >&2 + exit 255 + fi ;; + esac ;; + + # short options + + c) CONTINUE=1 ;; + d) DISCOVERONLY=1 ;; + i) INVARIANT=1 ;; + v) DEBUG=1 ;; + x) STOP=1 ;; + + h) usage false; exit 0 ;; + + *) # error + if [[ "$OPTERR" != 1 || "${optspec:0:1}" == ":" ]]; then + printf "Non-option argument: '-%s'\n" "$OPTARG" >&2 + exit 255 + fi ;; + esac + done + shift $((OPTIND-1)) + + + + # Finally parse an optional test function + + function="$1"; shift + case "$function" in + + # supported functions + + assert_raises) assert_raises "$1" "$2" ;; + assert_success) assert_success "$1" ;; + assert_failure) assert_failure "$1" ;; + + assert) assert "$1" "$2" ;; + assert_startswith) assert_startswith "$1" "$2" ;; + assert_endswith) assert_endswith "$1" "$2" ;; + assert_contains) assert_contains "$1" "$2" ;; + assert_NOTcontains) assert_NOTcontains "$1" "$2" ;; + assert_matches) assert_matches "$1" "$2" ;; + + assert_str_equals) assert_str_equals "$1" "$2" ;; + assert_str_NOTequals) assert_str_NOTequals "$1" "$2" ;; + assert_str_startswith) assert_str_startswith "$1" "$2" ;; + assert_str_endswith) assert_str_endswith "$1" "$2" ;; + assert_str_contains) assert_str_contains "$1" "$2" ;; + assert_str_NOTcontains) assert_str_NOTcontains "$1" "$2" ;; + assert_str_matches) assert_str_matches "$1" "$2" ;; + + # unsupported functions + + assert_end|skip|skip_if) + printf "Test function '%s' not supported as a script argument\n" \ + "$function" + exit 254 ;; + + '') ;; # no function + + *) # error + printf "Unknown test function: '%s'\n" "$function" + exit 255 ;; + esac + +} # parseArgs() + + +#------------------------------------------------------------------------------- +# usage +# +# Print usage information +# +usage() { + + cat <<- ENDUSAGE + Usage: $(basename "$0") [options] [assert_function] [arguments] + + > a handy unit testing framework < + + OPTION FLAGS + + -v, --verbose generate output for every individual test case + -x, --stop stop running tests after the first failure + -i, --invariant do not measure timings to remain invariant between runs + -d, --discover collect test suites only, do not run any tests + -c, --continue do not modify exit code to test suite status + + -h, --help display this help and exit + --version show version info and exit + ENDUSAGE + +} # usage() + + +#------------------------------------------------------------------------------- +# version +# +# Print version information +# +version() { + + cat <<- ENDVERSION + aserta v$_ASERTA_VERSION + + + + Copyright (C) 2016-2017 José Luis Cruz + Released under the MIT license. + Copyright (C) 2009-2015 Robert Lehmann + Released under the LGPLv3 license + + Dependencies found: + $__GREP, $__DATE + + ENDVERSION + +} #version() + + +#------------------------------------------------------------------------------- +# __gnuCmd +# +# Returns the resolved command, preferring the GNU versions +# and falling back to the standard version when not found. +# +__gnuCmd() { + local default_cmd="$1" + local gnu_cmd="g$default_cmd" + + if [[ "$(which "$gnu_cmd" 2> /dev/null)" ]]; then + printf '%s' "$gnu_cmd" + else + printf '%s' "$default_cmd" + fi + +} #__gnuCmd() + + +#------------------------------------------------------------------------------- +# _assert_cleanup +# +# gets called on script exit +# +_assert_cleanup() { + local status=$? + + # modify exit code if it's not already non-zero + [[ $status -eq 0 && -z $CONTINUE ]] && exit "$tests_suite_status" + +} # _assert_cleanup() + + +#------------------------------------------------------------------------------- +# _assert_fail +# +_assert_fail() { + [[ -n "$DEBUG" ]] && printf 'X' + report="test #$tests_ran \"$2${3:+ <<< $3}\" failed:${_indent}$1" + if [[ -n "$STOP" ]]; then + [[ -n "$DEBUG" ]] && printf '\n' + printf '%s\n' "$report" + exit 1 + fi + tests_errors[$tests_failed]="$report" + (( tests_failed++ )) || : + return 1 + +} # _assert_fail() + + +#------------------------------------------------------------------------------- +# _assert_reset +# +_assert_reset() { + tests_ran=0 + tests_failed=0 + tests_errors=() + tests_starttime="$($__DATE +%s%N)" # nanoseconds_since_epoch + +} # _assert_reset() + + +#------------------------------------------------------------------------------- +# _assert_with_grep +# +# NOTE: doesn't support newlines in the pattern +# +_assert_with_grep() { + local modifier="$1" + local string="$2" + local pattern="$3" + + assert_success "printf '$string' | $__GREP $modifier '$pattern'" || return 1 + +} # _assert_with_grep() + + + +# TEST FUNCTIONS: FLOW CONTROL +# ============================ + + +#------------------------------------------------------------------------------- +# skip_if +# +skip_if() { + (eval "$@") > /dev/null 2>&1 && status=0 || status="$?" + [[ "$status" -eq 0 ]] || return + skip + +} # skip_if() + + +#------------------------------------------------------------------------------- +# skip (no arguments) +# +skip() { + shopt -q extdebug && tests_extdebug=0 || tests_extdebug=1 + shopt -q -o errexit && tests_errexit=0 || tests_errexit=1 + + # enable extdebug so returning 1 in a DEBUG trap handler skips next command + shopt -s extdebug + + # disable errexit (set -e) so we can safely return 1 without causing exit + set +o errexit + tests_trapped=0 + trap _skip DEBUG + +} # skip() + + +#------------------------------------------------------------------------------- +# _skip +# +_skip() { + if [[ $tests_trapped -eq 0 ]]; then + # DEBUG trap for command we want to skip. Do not remove the handler + # yet because *after* the command we need to reset extdebug/errexit + # (in another DEBUG trap). + tests_trapped=1 + [[ -z "$DEBUG" ]] || printf 's' + return 1 + else + trap - DEBUG + [[ $tests_extdebug -eq 0 ]] || shopt -u extdebug + [[ $tests_errexit -eq 1 ]] || set -o errexit + return 0 + fi + +} # _skip() + + +#------------------------------------------------------------------------------- +# assert_end [suite ..] +# +assert_end() { + tests_endtime="$($__DATE +%s%N)" + + # required visible decimal place for seconds (leading zeros if needed) + local tests_time; tests_time="$( printf "%010d" \ + "$(( ${tests_endtime/%N/000000000} + - ${tests_starttime/%N/000000000} ))")" # in ns + tests="$tests_ran ${*:+$* }tests" + + [[ -n "$DISCOVERONLY" ]] && printf 'collected %s.\n' "$tests" \ + && _assert_reset && return + [[ -n "$DEBUG" ]] && printf '\n' + + # to get report_time split tests_time on 2 substrings: + # ${tests_time:0:${#tests_time}-9} - seconds + # ${tests_time:${#tests_time}-9:3} - milliseconds + if [[ -z "$INVARIANT" ]]; then + report_time=" in ${tests_time:0:${#tests_time}-9}.${tests_time:${#tests_time}-9:3}s" + else report_time=; fi + + if [[ "$tests_failed" -eq 0 ]]; then + printf 'all %s passed%s.\n' "$tests" "$report_time" + else + for error in "${tests_errors[@]}"; do printf '%s\n' "$error"; done + printf '%d of %s failed%s.\n' "$tests_failed" "$tests" "$report_time" + fi + + tests_ran_total=$((tests_ran_total + tests_ran)) + tests_failed_total=$((tests_failed_total + tests_failed)) + [[ $tests_failed -gt 0 ]] && tests_suite_status=1 + _assert_reset + +} # assert_end() + + + +# TEST FUNCTIONS: RETURN STATUS +# ============================= + + +#------------------------------------------------------------------------------- +# assert_raises [stdin] +# +assert_raises() { + (( tests_ran++ )) || : + [[ -z "$DISCOVERONLY" ]] || return + status=0 + (eval "$1" <<< "${3:-}") > /dev/null 2>&1 || status="$?" + expected=${2:-0} + if [[ "$status" -eq "$expected" ]]; then + [[ -z "$DEBUG" ]] || printf '.' + return + fi + _assert_fail "program terminated with code $status instead of $expected" \ + "$1" "$3" + +} # assert_raises() + + +#------------------------------------------------------------------------------- +# assert_success [stdin] +# +assert_success() { + assert_raises "$1" 0 "${2:-}" +} + + +#------------------------------------------------------------------------------- +# assert_failure [stdin] +# +assert_failure() { + (( tests_ran++ )) || : + [[ -z "$DISCOVERONLY" ]] || return + status=0 + (eval "$1" <<< "${2:-}") > /dev/null 2>&1 || status="$?" + if [[ "$status" != "0" ]]; then + [[ -z "$DEBUG" ]] || printf '.' + return + fi + _assert_fail "program terminated with a zero return code; expecting non-zero return code" \ + "$1" "$2" + +} # assert_failure() + + + +# TEST FUNCTIONS: EXPECTED OUTPUT +# =============================== + + +#------------------------------------------------------------------------------- +# assert [stdin] +# +assert() { + (( tests_ran++ )) || : + [[ -z "$DISCOVERONLY" ]] || return + # shellcheck disable=SC2059 + expected=$(printf "${2:-}") + # shellcheck disable=SC2059 + result=$(printf "$(eval 2>/dev/null "$1" <<< "${3:-}")") + if [[ "$result" == "$expected" ]]; then + [[ -z "$DEBUG" ]] || printf '.' + return + fi + result="$(sed -e :a -e '$!N;s/\n/\\n/;ta' <<< "$result")" + [[ -z "$result" ]] && result="nothing" || result="\"$result\"" + [[ -z "$2" ]] && expected="nothing" || expected="\"$2\"" + _assert_fail "expected $expected${_indent}got $result" "$1" "${3:-}" + +} # assert() + + +#------------------------------------------------------------------------------- +# assert_contains +# +assert_contains() { + assert_success "[[ '$($1)' == *'$2'* ]]" +} + +#------------------------------------------------------------------------------- +# assert_NOTcontains +# +assert_NOTcontains() { + assert_success "[[ '$($1)' != *'$2'* ]]" +} + + +#------------------------------------------------------------------------------- +# assert_startswith +# +assert_startswith() { + assert_success "[[ '$($1)' == '$2'* ]]" +} + + +#------------------------------------------------------------------------------- +# assert_endswith +# +assert_endswith() { + assert_success "[[ '$($1)' == *'$2' ]]" +} + + +#------------------------------------------------------------------------------- +# assert_matches +# +assert_matches() { + _assert_with_grep '-E' "$($1)" "$2" +} + + + +# TEST FUNCTIONS: STRING COMPARISON +# ============================ + + +#------------------------------------------------------------------------------- +# assert_str_equals +# +assert_str_equals() { + assert_success "[[ '$1' == '$2' ]]" +} + + +#------------------------------------------------------------------------------- +# assert_str_NOTequals +# +assert_str_NOTequals() { + assert_success "[[ '$1' != '$2' ]]" +} + + +#------------------------------------------------------------------------------- +# assert_str_startswith +# +assert_str_startswith() { + assert_success "[[ '$1' == '$2'* ]]" +} + + +#------------------------------------------------------------------------------- +# assert_str_endswith +# +assert_str_endswith() { + assert_success "[[ '$1' == *'$2' ]]" +} + + +#------------------------------------------------------------------------------- +# assert_str_contains +# +assert_str_contains() { + assert_success "[[ '$1' == *'$2'* ]]" +} + +#------------------------------------------------------------------------------- +# assert_str_NOTcontains +# +assert_str_NOTcontains() { + assert_success "[[ '$1' != *'$2'* ]]" +} + + +#------------------------------------------------------------------------------- +# assert_str_matches +# +assert_str_matches() { + _assert_with_grep '-E' "$1" "$2" +} + + +main "$@" diff --git a/package.json b/package.json new file mode 100644 index 00000000..6a2f8985 --- /dev/null +++ b/package.json @@ -0,0 +1,32 @@ +{ + "name": "rexreplace", + "version": "1.0.0", + "description": "CLI regexp replacement for files with lookahead and backreference to matching groups in the replacement.", + "author": "Mathias Rangel Wulff", + "license": "MIT", + "main": "rexreplace-cli.js", + "repository": { + "type": "git", + "url": "https://github.com/mathiasrw/rexreplace/" + }, + "bin": { + "rexreplace": "./rexreplace-cli.js" + }, + "scripts": { + "test": "npm -g install ./ && bash test.sh" + }, + "keywords": [ + "regexp", + "text", + "replace", + "CLI", + "regular", + "expression" + ], + "devDependencies": { + }, + "dependencies": { + "chalk": "^1.1.3", + "yargs": "^7.0.2" + } +} diff --git a/rexreplace-cli.js b/rexreplace-cli.js new file mode 100644 index 00000000..829e49c7 --- /dev/null +++ b/rexreplace-cli.js @@ -0,0 +1,211 @@ +#!/usr/bin/env node + +const fs = require('fs'); +const path = require('path'); +const version = "1.0.0"; +const font = require('chalk'); + +let yargs = require('yargs') + .strict() + + .usage('RexReplace '+version+': Regexp search and replace for files using lookahead and backreference to matching groups in the replacement. Defaults to global multiline case-insensitive search.\n\n> rexreplace searchFor replaceWith filename') + + .example("> rexreplace '(f?(o))o(.*)' '$3$1$2' myfile.md", "'foobar' in myfile.md will become 'barfoo'") + .example('') + .example("> rexreplace -I 'Foo' 'xxx' myfile.md", "'foobar' in myfile.md will remain 'foobar'") + + .version('v', 'Echo rexreplace version', version) + .alias('v', 'version') + + .boolean('I') + .describe('I', 'Void case insensitive search pattern.') + .alias('I', 'void-ignore-case') + + .boolean('M') + .describe('M', 'Void multiline search pattern. Makes ^ and $ match start/end of whole content rather than each line.') + .alias('M', 'void-multiline') + + .boolean('u') + .describe('u', 'Treat pattern as a sequence of unicode code points.') + .alias('u', 'unicode') + + .describe('e', 'Encoding of files.') + .alias('e', 'encoding') + .default('e', 'utf8') + + .boolean('o') + .describe('o', 'Output the result instead of saving to file. Will also output content even if no replacement have taken place.') + .alias('o', 'output') + //.conflicts('o', 'd') + + .boolean('q') + .describe('q', "Only display erros (no other info)") + .alias('q', 'quiet') + + .boolean('Q') + .describe('Q', "Never display erros or info") + .alias('Q', 'quiet-total') + + .boolean('H') + .describe('H', "Halt on first error") + .alias('H', 'halt') + .default('H', false) + + .boolean('d') + .describe('d', "Print debug info") + .alias('d', 'debug') + + .boolean('€') + .describe('€', "Replace all '€' with '$' in pattern and replace string. Usefull when your commandline (bash/zsh/...) seeks to do interesting things with '$'") + .alias('€', 'eurodollar') + + /* // Ideas + + .boolean('n') + .describe('n', "Do replacement on file names instead of file content (rename the files)") + .alias('n', 'name') + + + .boolean('€') + .describe('€', "Stop replacing '€' with '$' in pattern and replacement") + .alias('€', 'void-euro') + + .boolean('d') + .describe('d', 'Dump matches as json output') + .alias('d', 'dump') + + + .boolean('v') + .describe('v', "More chatty output") + .alias('v', 'verbose') + + + .boolean('n') + .describe('n', "Do replacement on file names instead of file content (rename the files)") + .alias('n', 'name') + */ + + .help('h') + .alias('h', 'help') + + .epilog('What "sed" should have been...') + +; + +const args = yargs.argv; + +debug(args); + +if(args._.length<3){ + die('Need more than 2 arguments',args._.length+' was found',true); +} + +if(args['€']){ + args._[0] = args._[0].replace('€','$'); + args._[1] = args._[1].replace('€','$'); +} + +let flags = 'g'; +if(!args['void-ignore-case']){ + flags += 'i'; +} +if(!args['void-multiline']){ + flags += 'm'; +} +if(args.unicode){ + flags += 'u'; +} + +debug(flags); + +// Get regex pattern +let regex = args._.shift(); +try{ + regex = new RegExp(regex,flags); +} catch (err){ + die('Wrong formatted regexp', regex); +} + +// Get replacement +const replace = args._.shift(); + +// The rest are files +const files = args._; + +files + // Correct filepath + .map(filepath=>path.normalize(process.cwd()+'/'+filepath)) + + // Find out if any filepaths are invalid + .filter(filepath=>fs.existsSync(filepath)?true:error('File not found:',filepath)) + + // Do the replacement + .forEach(filepath=>replaceInFile(filepath,regex,replace,args.encoding)) +; + + +function replaceInFile(file,regex,replace,encoding){ + fs.readFile(file, encoding, function (err,data) { + if (err) { + return error(err); + } + debug('About to replace in: '+file); + const result = data.replace(regex, replace); + + if(args.output){ + debug('Outputting result from: '+file); + return process.stdout.write(result); + } + + // Nothing replaced = no need for writing file again + if(result === data){ + debug('Nothing changed in: '+file); + return; + } + + debug('About to write to: '+file); + fs.writeFile(file, result, encoding, function (err) { + if (err){ + return error(err); + } + info(file); + }); + }); +} + +function info(msg, data=''){ + if(args.quiet || args['quiet-total']){ + return; + } + console.log(font.green(msg), data); +} + +function die(msg, data='', displayHelp=false){ + if(displayHelp){ + yargs.showHelp(); + } + error(msg, data); + kill(); +} + +function error(msg, data=''){ + if(!args.quiet && !args['quiet-total']){ + console.error(font.red(msg), data); + } + if(args.halt){ + kill(); + } + return false; +} + +function debug(data){ + if(args.debug){ + console.log(font.gray(JSON.stringify(data, null,4))); + } +} + +function kill(error=1){ + setTimeout(()=>process.exit(error),10); // give stdout a bit of time to finish +} + + diff --git a/test.sh b/test.sh new file mode 100644 index 00000000..2f9a27f4 --- /dev/null +++ b/test.sh @@ -0,0 +1,99 @@ +#!/usr/bin/env bash + +source aserta.sh + +# # Command exit codes +# assert_success "true" +# assert_failure "false" +# assert_raises "unknown_cmd" 127 +# +# Expected output +# assert "echo test" "test" +# assert "seq 3" "1\n2\n3" +# assert_contains "echo foobar" "oba" +# assert_startswith "echo foobar" "foo" +# assert_endswith "echo foobar" "bar" +# assert_matches "echo foobar" "^f.*r$" +# +# assert_end "example" +reset() { + echo 'foobar' > myfile +} + + +reset +assert_success "rexreplace -version" + +reset +assert_success "rexreplace -help" + +reset +rexreplace x x myfile -Q +assert "cat myfile " "foobar" + +reset +rexreplace o x myfile -Q +assert "cat myfile" "fxxbar" + +reset +assert "rexreplace x x myfile --output" "foobar" + +reset +assert "rexreplace o x myfile --output" "fxxbar" + +reset +assert "rexreplace '.€' € myfile -o --eurodollar" 'fooba$' + +reset +assert "rexreplace '.€' € myfile -o -€" 'fooba$' + +reset +assert "rexreplace '(f?(o))o(.*)' '\$3\$1\$2' myfile -o" "barfoo" + +reset +assert "rexreplace Foo xxx myfile -o" "xxxbar" + +reset +assert "rexreplace Foo xxx myfile -o --void-ignore-case" "foobar" + +reset +echo foobar >> myfile +assert "rexreplace '^.' 'x' myfile -o" "xoobar\nxoobar" + +reset +echo foobar >> myfile +assert "rexreplace '^.' 'x' myfile -o --void-multiline" "xoobar\nfoobar" + +# Todo: test -e +# assert "rexreplace ??? ??? myfile -e" "foobar" +# reset + +# Todo: test -q +# assert "rexreplace ??? ??? myfile -q" "foobar" +# reset + +# Todo: test -Q +# assert "rexreplace ??? ??? myfile -Q" "foobar" +# reset + +# Todo: test -H +# assert "rexreplace ??? ??? myfile -H" "foobar" +# reset + +# Todo: test -d +# assert "rexreplace ??? ??? myfile -d" "foobar" +# reset + +reset + +assert_end "rexreplace" + + + + + + + + + +