From 3383b5c73f8493c1f6eb9405651f9dd5b1c5a42a Mon Sep 17 00:00:00 2001 From: Aaron Fiore Date: Tue, 30 Sep 2025 17:43:25 -0700 Subject: [PATCH] container: migrate from `xsv` to `xan` `xsv` is no longer maintained and the author recommends `xan` instead. Fortunately, `xan` is very suitable for our use-case; is very efficient, and produces no differences in our expected output. --- container/src/finance/lib/internal/lib_meta.bash | 4 ++-- .../src/finance/lib/internal/lib_taxes.bash | 16 ++++++++-------- .../accounts/bittrex/bittrex-shared.bash | 8 ++++---- .../accounts/celsius/celsius-shared.bash | 8 ++++---- .../coinbase-pro/coinbase-pro-shared.bash | 4 ++-- .../accounts/coinbase/coinbase-shared.bash | 6 +++--- .../accounts/gemini/gemini-shared.bash | 6 +++--- .../paypal-business/paypal-business-shared.bash | 4 ++-- .../accounts/paypal/paypal-shared.bash | 6 +++--- 9 files changed, 31 insertions(+), 31 deletions(-) diff --git a/container/src/finance/lib/internal/lib_meta.bash b/container/src/finance/lib/internal/lib_meta.bash index e651d94..a4905ea 100644 --- a/container/src/finance/lib/internal/lib_meta.bash +++ b/container/src/finance/lib/internal/lib_meta.bash @@ -65,7 +65,7 @@ function lib_meta::__parse_args() \e[32mAvailable columns:\e[0m -$(echo $global_meta_header | xsv headers -) +$(echo $global_meta_header | xan headers -) \e[32mExamples:\e[0m @@ -123,7 +123,7 @@ function lib_meta::__meta() # Nth iteration (select column and pattern) local _tmp_file _tmp_file="$(mktemp -p $_tmp_dir ${_arg}_XXX)" - xsv search -i -s \ + xan search -i -s \ "${_arg%${global_arg_delim_2}*}" "${_arg#*${global_arg_delim_2}}" \ "$_base_file" >"$_tmp_file" diff --git a/container/src/finance/lib/internal/lib_taxes.bash b/container/src/finance/lib/internal/lib_taxes.bash index 5a0b41f..b4274cf 100644 --- a/container/src/finance/lib/internal/lib_taxes.bash +++ b/container/src/finance/lib/internal/lib_taxes.bash @@ -430,7 +430,7 @@ function lib_taxes::__taxes_print() if [[ "$_tag" != "MATCH" && "$_tag" != "PARTIAL_TRADE" ]]; then "${_hledger[@]}" \ - | xsv select '"posting-comment"' \ + | xan select '"posting-comment"' \ | tail -n +2 \ | sed -e 's:"::g' -e '/^$/d' \ | gawk -v tag="$_tag" -v is_trades="$_is_trades" \ @@ -607,7 +607,7 @@ function lib_taxes::__taxes_print() if [[ "$_tag" == "MATCH" ]]; then "${_hledger[@]}" \ - | xsv select '"posting-comment"' \ + | xan select '"posting-comment"' \ | tail -n +2 \ | sed -e 's:"::g' -e '/^$/d' \ | sort -u \ @@ -856,7 +856,7 @@ function lib_taxes::__taxes_print() if [[ "$_tag" == "PARTIAL_TRADE" ]]; then "${_hledger[@]}" \ - | xsv select '"posting-comment"' \ + | xan select '"posting-comment"' \ | tail -n +2 \ | sed -e 's:"::g' -e '/^$/d' \ | sort -u \ @@ -1002,7 +1002,7 @@ function lib_taxes::__taxes_write() if [ -z "$global_arg_year" ]; then lib_utils::print_normal "Capturing the year of the oldest tagged record ..." - local -r _oldest_year="$("${_base_hledger[@]}" | head -n2 | xsv select \"date\" | tail -n1 | cut -d'-' -f1)" + local -r _oldest_year="$("${_base_hledger[@]}" | head -n2 | xan select \"date\" | tail -n1 | cut -d'-' -f1)" [ -z "$_oldest_year" ] && lib_utils::die_fatal "No records available to write" elif [[ "$global_arg_year" -gt "$_current_year" ]]; then @@ -1247,7 +1247,7 @@ function lib_taxes::__reports_patch_verify() [ ! -f "$_file" ] && lib_utils::die_fatal "File not found: '${_file}'" if [ -s "$_file" ]; then - xsv select "Action" "$_file" \ + xan select "Action" "$_file" \ | sort -u \ | tail -n +2 \ | while read _line; do @@ -1295,7 +1295,7 @@ function lib_taxes::__reports_obfs_gen() # Generate obfs/raw keymap local _raw_column - _raw_column="$(xsv select $_column $_in_file | tail -n +2 | sort -u)" + _raw_column="$(xan select $_column $_in_file | tail -n +2 | sort -u)" local _count _count=$(echo "$_raw_column" | wc -l) @@ -1352,7 +1352,7 @@ function lib_taxes::__reports_obfs_gen() # Generate a temp file of the given raw column local _tmp_file="${_tmp_dir}/${_styled_column}" - xsv select "$_column" "$_in_file" -o "$_tmp_file" + xan select "$_column" "$_in_file" -o "$_tmp_file" # Obfuscate given raw column with keymap data tail -n +2 "$_keymap_file" \ @@ -1364,7 +1364,7 @@ function lib_taxes::__reports_obfs_gen() # Join the obfuscated temp files into out file and then delete the temp files. # NOTE: selecting will remove the raw (unobfuscated) columns csvjoin -I --snifflimit 0 "${_tmp_dir}"/* "$_in_file" \ - | xsv select "$_all_columns" -o "$_out_file" + | xan select "$_all_columns" -o "$_out_file" } # vim: sw=2 sts=2 si ai et diff --git a/container/src/hledger-flow/accounts/bittrex/bittrex-shared.bash b/container/src/hledger-flow/accounts/bittrex/bittrex-shared.bash index 075641d..7145868 100755 --- a/container/src/hledger-flow/accounts/bittrex/bittrex-shared.bash +++ b/container/src/hledger-flow/accounts/bittrex/bittrex-shared.bash @@ -2,7 +2,7 @@ # docker-finance | modern accounting for the power-user # -# Copyright (C) 2021-2024 Aaron Fiore (Founder, Evergreen Crypto LLC) +# Copyright (C) 2021-2025 Aaron Fiore (Founder, Evergreen Crypto LLC) # # This program is free software: you can redistribute it and/or modify # it under the terms of the GNU General Public License as published by @@ -43,7 +43,7 @@ source "${DOCKER_FINANCE_CONTAINER_REPO}/src/hledger-flow/lib/lib_preprocess.bas function parse_deposit() { # NOTE: headers can be variable, not possible to assert - xsv select "id,completedAt,currencySymbol,quantity,cryptoAddress,txId" "$global_in_path" \ + xan select "id,completedAt,currencySymbol,quantity,cryptoAddress,txId" "$global_in_path" \ | gawk -v global_year="$global_year" -v global_subaccount="$global_subaccount" \ '{ if (NR<2 || $2 !~ global_year) @@ -79,7 +79,7 @@ function parse_trade() # WARNING: don't use limit because market orders will be empty # NOTE: headers can be variable, not possible to assert - xsv select "id,closedAt,direction,marketSymbol,fillQuantity,proceeds,commission" "$global_in_path" \ + xan select "id,closedAt,direction,marketSymbol,fillQuantity,proceeds,commission" "$global_in_path" \ | gawk -v global_year="$global_year" -v global_subaccount="$global_subaccount" \ '{ if (NR<2 || $2 !~ global_year) @@ -130,7 +130,7 @@ function parse_trade() function parse_withdrawal() { # NOTE: headers can be variable, not possible to assert - xsv select "id,completedAt,currencySymbol,quantity,txCost,cryptoAddress,txId" "$global_in_path" \ + xan select "id,completedAt,currencySymbol,quantity,txCost,cryptoAddress,txId" "$global_in_path" \ | gawk -v global_year="$global_year" -v global_subaccount="$global_subaccount" \ '{ if (NR<2 || $2 !~ global_year) diff --git a/container/src/hledger-flow/accounts/celsius/celsius-shared.bash b/container/src/hledger-flow/accounts/celsius/celsius-shared.bash index 39f167b..fb1d4a3 100755 --- a/container/src/hledger-flow/accounts/celsius/celsius-shared.bash +++ b/container/src/hledger-flow/accounts/celsius/celsius-shared.bash @@ -2,7 +2,7 @@ # docker-finance | modern accounting for the power-user # -# Copyright (C) 2021-2024 Aaron Fiore (Founder, Evergreen Crypto LLC) +# Copyright (C) 2021-2025 Aaron Fiore (Founder, Evergreen Crypto LLC) # # This program is free software: you can redistribute it and/or modify # it under the terms of the GNU General Public License as published by @@ -44,7 +44,7 @@ function parse_ui() { lib_preprocess::assert_header "Internal id, Date and time, Transaction type, Coin type, Coin amount, USD Value, Original Reward Coin, Reward Amount In Original Coin, Confirmed" - xsv search -s " Date and time" "$global_year" "$global_in_path" \ + xan search -s " Date and time" "$global_year" "$global_in_path" \ | sed -r 's/ ([0-9]+),/ \1/' \ | gawk -v global_subaccount="$global_subaccount" -M -v PREC=100 \ '{ @@ -116,8 +116,8 @@ function parse_api() { lib_preprocess::assert_header "id,amount,amount_precise,amount_usd,coin,state,nature,time,tx_id,original_interest_coin,interest_amount_in_original_coin" - xsv select "id,time,nature,coin,amount_precise,amount_usd,original_interest_coin,interest_amount_in_original_coin,state,tx_id" "$global_in_path" \ - | xsv search -s "time" "$global_year" \ + xan select "id,time,nature,coin,amount_precise,amount_usd,original_interest_coin,interest_amount_in_original_coin,state,tx_id" "$global_in_path" \ + | xan search -s "time" "$global_year" \ | gawk -v global_subaccount="$global_subaccount" -M -v PREC=100 \ '{ if (NR<2) diff --git a/container/src/hledger-flow/accounts/coinbase-pro/coinbase-pro-shared.bash b/container/src/hledger-flow/accounts/coinbase-pro/coinbase-pro-shared.bash index eb4c730..da35744 100755 --- a/container/src/hledger-flow/accounts/coinbase-pro/coinbase-pro-shared.bash +++ b/container/src/hledger-flow/accounts/coinbase-pro/coinbase-pro-shared.bash @@ -2,7 +2,7 @@ # docker-finance | modern accounting for the power-user # -# Copyright (C) 2021-2024 Aaron Fiore (Founder, Evergreen Crypto LLC) +# Copyright (C) 2021-2025 Aaron Fiore (Founder, Evergreen Crypto LLC) # # This program is free software: you can redistribute it and/or modify # it under the terms of the GNU General Public License as published by @@ -57,7 +57,7 @@ function parse_trade() cat "$global_in_path" \ | if [ ! -z "$_details_transfer_id" ]; then csvjoin -I --snifflimit 0 - <(echo "$_details_transfer_id"); else cat; fi \ | if [ ! -z "$_details_transfer_type" ]; then csvjoin -I --snifflimit 0 - <(echo "$_details_transfer_type"); else cat; fi \ - | xsv select "id,amount,balance,created_at,type,details_transfer_id,details_transfer_type,details_order_id,details_product_id,details_trade_id" \ + | xan select "id,amount,balance,created_at,type,details_transfer_id,details_transfer_type,details_order_id,details_product_id,details_trade_id" \ | gawk -v account_id="$account_id" -v account_currency="$account_currency" \ -v global_year="$global_year" -v global_subaccount="$global_subaccount" \ '{ diff --git a/container/src/hledger-flow/accounts/coinbase/coinbase-shared.bash b/container/src/hledger-flow/accounts/coinbase/coinbase-shared.bash index c4ac0a4..f3a6411 100755 --- a/container/src/hledger-flow/accounts/coinbase/coinbase-shared.bash +++ b/container/src/hledger-flow/accounts/coinbase/coinbase-shared.bash @@ -60,7 +60,7 @@ function add_to_headers() function join_to_header() { # WARNING: csvjoin *MUST* use -I or else satoshis like 0.00000021 will turn into 21000000 BTC! - # TODO: `xsv join` is significantly faster but does not produce same results + # TODO: will `xan join` be faster and produce the same results? csvjoin -I --snifflimit 0 "$1" <(echo "$2") } @@ -318,9 +318,9 @@ function parse() # NOTE: prepends account_id to header (this will now be the first column) - xsv select "$selected_header" "$global_in_path" \ + xan select "$selected_header" "$global_in_path" \ | join_to_header - "$universal_header" \ - | xsv select "$universal_header" \ + | xan select "$universal_header" \ | sed -e "s:^:${_account_id},:g" -e "1 s:^${_account_id},:account_id,:g" \ | __parse >"$global_out_path" } diff --git a/container/src/hledger-flow/accounts/gemini/gemini-shared.bash b/container/src/hledger-flow/accounts/gemini/gemini-shared.bash index 5f6aa0c..aab1d08 100755 --- a/container/src/hledger-flow/accounts/gemini/gemini-shared.bash +++ b/container/src/hledger-flow/accounts/gemini/gemini-shared.bash @@ -45,7 +45,7 @@ source "${DOCKER_FINANCE_CONTAINER_REPO}/src/hledger-flow/lib/lib_preprocess.bas function parse_transfers() { # NOTE: header can be variable, ineffective to assert_header - # TODO: optimize: do successive test_headers and whichever wins, use that string so `xsv select` is avoided + # TODO: optimize: do successive test_headers and whichever wins, use that string so `xan select` is avoided local _header="" if lib_preprocess::test_header "info_feeCurrency"; then @@ -59,7 +59,7 @@ function parse_transfers() # Off-chain (withdrawal) lib_preprocess::test_header "info_method" && _header+=",info_method" - xsv select "$_header" "$global_in_path" \ + xan select "$_header" "$global_in_path" \ | gawk -v global_year="$global_year" -v global_subaccount="$global_subaccount" -M -v PREC=100 \ '{ if (NR<2) @@ -130,7 +130,7 @@ function parse_transfers() # Off-chain withdrawal lib_preprocess::test_header "info_method" && _header+=",info_method" - xsv select "$_header" "$global_in_path" \ + xan select "$_header" "$global_in_path" \ | gawk -v global_year="$global_year" -v global_subaccount="$global_subaccount" -M -v PREC=100 \ '{ if (NR<2) diff --git a/container/src/hledger-flow/accounts/paypal-business/paypal-business-shared.bash b/container/src/hledger-flow/accounts/paypal-business/paypal-business-shared.bash index 5c0fff7..1ac7788 100755 --- a/container/src/hledger-flow/accounts/paypal-business/paypal-business-shared.bash +++ b/container/src/hledger-flow/accounts/paypal-business/paypal-business-shared.bash @@ -41,8 +41,8 @@ function parse() lib_preprocess::assert_header "$_header" # Paypal is known to allow commas within description ("Name") and amounts - # TODO: refactor xsv/sed -> gawk (the sed line should remove non-csv commas and quotations) - xsv select "$(echo "$_header" | sed 's:"::g')" $global_in_path \ + # TODO: refactor xan/sed -> gawk (the sed line should remove non-csv commas and quotations) + xan select "$(echo "$_header" | sed 's:"::g')" $global_in_path \ | sed -e 's:, : :g' -e 's:,",:,:g' -e 's:,"\([0-9]*\),:,\1:g' -e 's:,"-\([0-9]*\),:,\1:g' -e 's:"::g' \ | gawk -v global_year="$global_year" -v global_subaccount="$global_subaccount" \ '{ diff --git a/container/src/hledger-flow/accounts/paypal/paypal-shared.bash b/container/src/hledger-flow/accounts/paypal/paypal-shared.bash index 19ed2bc..03a67d5 100755 --- a/container/src/hledger-flow/accounts/paypal/paypal-shared.bash +++ b/container/src/hledger-flow/accounts/paypal/paypal-shared.bash @@ -47,8 +47,8 @@ function parse_report() # Paypal is known to allow commas within description ("Name") and amounts # NOTE: using custom timezone offset instead of provided timezone - # TODO: refactor xsv/sed -> gawk (the sed line should remove non-csv commas and quotations) - xsv select "Date,Time,Name,Type,Status,Currency,Amount,Fees,Total,Exchange Rate,Receipt ID,Balance,Transaction ID,Item Title" "$global_in_path" \ + # TODO: refactor xan/sed -> gawk (the sed line should remove non-csv commas and quotations) + xan select "Date,Time,Name,Type,Status,Currency,Amount,Fees,Total,Exchange Rate,Receipt ID,Balance,Transaction ID,Item Title" "$global_in_path" \ | sed -e 's:, : :g' -e 's:,",:,:g' -e 's:,"\([0-9]*\),:,\1:g' -e 's:,"-\([0-9]*\),:,-\1:g' -e 's:"::g' \ | gawk -v global_year="$global_year" -v global_subaccount="$global_subaccount" \ '{ @@ -121,7 +121,7 @@ function parse_crypto() #lib_preprocess::assert_header "DateTime,Transaction Type,Asset In (Quantity),Asset In (Currency),Asset Out (Quantity),Asset Out (Currency),Transaction Fee (Quantity),Transaction Fee (Currency),Market Value (USD)" "$(sed -n '4p' $global_in_path)" # TODO: refactor into gawk after assert_header is fixed - xsv select "DateTime,Transaction Type,Asset In (Quantity),Asset In (Currency),Asset Out (Quantity),Asset Out (Currency),Transaction Fee (Quantity),Transaction Fee (Currency),Market Value (USD)" <(tail +4 "$global_in_path") \ + xan select "DateTime,Transaction Type,Asset In (Quantity),Asset In (Currency),Asset Out (Quantity),Asset Out (Currency),Transaction Fee (Quantity),Transaction Fee (Currency),Market Value (USD)" <(tail +4 "$global_in_path") \ | gawk -v global_year="$global_year" -v global_subaccount="$global_subaccount" \ '{ if (NR<2 || $1 !~ global_year)