一次 ChatGPT 的“调教”过程

让 AI 成为你的工作好搭档。


AI 生成大火,咱得把这先进工具用起来!

程序员的一个日常场景是,要写一些临时脚本来完成临时的事情。比如,我在做性能自测实验时,需要把搜集的性能数据文件,从测试环境的 k8s pod 里拷贝到本机。

于是开始了一次 ChatGPT 的“调教”过程。

从 K8S pod 到 K8S 宿主机

ME【提问】:

在容器 A 上有 ids_111.txt, ids_112.txt , ... ids_120.txt 10 个文件, 用 shell 批量将这些文件拷贝到其宿主机上。

ChatGPT3【回答】:

你可以使用以下Shell脚本将容器A上的这些文件拷贝到宿主机上:

#!/bin/bash

# 指定容器A的名称
CONTAINER_NAME="containerA"

# 指定宿主机上存放文件的目录
HOST_DIR="/path/to/host/directory"

# 拷贝文件
for ((i=111; i<=120; i++))
do
    CONTAINER_FILE="ids_${i}.txt"
    HOST_FILE="${HOST_DIR}/ids_${i}.txt"

    docker cp ${CONTAINER_NAME}:/path/to/files/${CONTAINER_FILE} ${HOST_FILE}
done

在此脚本中,你需要将`CONTAINER_NAME`和`HOST_DIR`替换为你实际使用的容器名称和宿主机目录路径。脚本使用`docker cp`命令来将文件从容器A拷贝到宿主机上,循环处理每个文件,文件名根据循环动态生成。

确保在运行脚本之前已经安装好Docker,并且已经启动了容器A。运行脚本后,文件将被拷贝到指定的宿主机目录中。

ME:

显然,AI 是从 docker 环境去考虑的,不符合我的场景。于是,明确上下文语境,再次提问:

在 k8s 上有一个 pod deploy-ids-detect-7b777ff97b-ncrqb , 这个 pod 下 有一些文件,文件命名为 ids_method_costs_yyyyMMddHHmmssSSSS.txt,比如 ids_method_costs_20230719122500042.txt , 如何将这些文件批量拷贝到宿主机上。

ChatGPT3【回答】:

你可以使用kubectl命令和kubectl cp命令将Kubernetes Pod中的文件批量拷贝到宿主机上。以下是一个示例的Shell脚本:

#!/bin/bash

# 指定Pod名称
POD_NAME="deploy-ids-detect-7b777ff97b-ncrqb"

# 指定容器名称,默认为Pod的第一个容器
CONTAINER_NAME=$(kubectl get pods ${POD_NAME} -o jsonpath="{.spec.containers[0].name}")

# 指定宿主机上存放文件的目录
HOST_DIR="/path/to/host/directory"

# 拷贝文件
kubectl exec ${POD_NAME} -c ${CONTAINER_NAME} -- bash -c 'ls /path/to/files/ids_method_costs_*.txt' | \
    while read -r FILE; do
        FILENAME=$(basename ${FILE})
        HOST_FILE="${HOST_DIR}/${FILENAME}"
        kubectl cp ${POD_NAME}:${FILE} ${HOST_FILE}
    done

在脚本中,你需要将`POD_NAME`和`HOST_DIR`替换为你实际使用的Pod名称和宿主机目录路径。默认情况下,脚本使用Pod中的第一个容器进行文件拷贝,如果你需要指定其他容器,可以修改`CONTAINER_NAME`变量。

脚本首先使用`kubectl exec`命令列出Pod中的指定文件列表,然后逐行读取文件列表并使用`kubectl cp`命令将文件拷贝到宿主机上。

确保在运行脚本之前已经安装并配置好kubectl,同时Pod和容器都处于运行状态。运行脚本后,文件将被批量拷贝到指定的宿主机目录中。

做了一点点修改,最终脚本如下:

#!/bin/bash

# 指定Pod名称
POD_NAME=$(kubectl get po -A | grep ids-detect | awk '{print $2}')
NAMESPACE=$(kubectl get po -A | grep ids-detect | awk '{print $1}')

# 指定容器名称,默认为Pod的第一个容器
CONTAINER_NAME=main

# 指定宿主机上存放文件的目录
HOST_DIR="/tmp"

# 拷贝文件
kubectl exec ${POD_NAME} -n $NAMESPACE  -c ${CONTAINER_NAME} -- bash -c 'ls /tmp/ids_method_costs_*.txt' | \
    while read -r FILE; do
        FILENAME=$(basename ${FILE})
        HOST_FILE="${HOST_DIR}/${FILENAME}"
        echo "kubectl cp ${POD_NAME}:${FILE} -n $NAMESPACE -c ${CONTAINER_NAME} ${HOST_FILE}"
        kubectl cp ${POD_NAME}:${FILE} -n $NAMESPACE -c ${CONTAINER_NAME} ${HOST_FILE}
    done

这里:

  • 咱的 K8S 环境操作 Pod 时必须指定 namespace。
  • 使用 $(kubectl get po -A | grep ids-detect | awk '{print $2}') 获取 pod 名称,适配任何 K8S 环境。
  • 用 echo 打印命令语句,容易调试。

从 K8S 宿主机拷贝到本机

ME【再次提问】:

在宿主机 A 上有一些文件,文件命名为 ids_method_costs_yyyyMMddHHmmssSSSS.txt 比如 ids_method_costs_20230719122500042.txt 。 现在要把这些文件拷贝到宿主机 B 上。 A ping B 不通, 但 B 能 ping 通 A 。 写一个脚本,将这些文件从 A 拷贝到 B ,这个脚本需要在 B 上执行。

ChatGPT3【回答】:

如果宿主机A能够ping通宿主机B,但宿主机B无法ping通宿主机A,你可以在宿主机B上执行一个脚本,使用SCP命令从宿主机A拷贝文件到宿主机B。以下是一个示例的Shell脚本:

#!/bin/bash

# 指定宿主机A的IP地址和登录凭证
HOST_A="IP_ADDRESS_OF_HOST_A"
USER_A="USERNAME_OF_HOST_A"
PASSWORD_A="PASSWORD_OF_HOST_A"

# 指定宿主机B上存放文件的目录
HOST_B_DIR="/path/to/host_b/directory"

# 指定宿主机A上存放文件的目录
HOST_A_DIR="/path/to/host_a/directory"

# 拷贝文件
for file in ${HOST_A_DIR}/ids_method_costs_*.txt; do
    filename=$(basename ${file})
    sshpass -p ${PASSWORD_A} scp ${USER_A}@${HOST_A}:${file} ${HOST_B_DIR}/${filename}
done

在脚本中,你需要将`HOST_A`、`USER_A`和`PASSWORD_A`分别替换为宿主机A的IP地址、登录用户名和登录密码。同时,将`HOST_B_DIR`替换为宿主机B上存放文件的目录,以及`HOST_A_DIR`替换为宿主机A上存放文件的目录。

脚本使用`sshpass`命令配合`scp`命令从宿主机A拷贝文件到宿主机B。在循环中,脚本会遍历宿主机A上的文件列表,逐个拷贝到宿主机B的指定目录中。

在宿主机B上执行该脚本时,确保已经安装了`sshpass`工具,并且能够通过SSH连接到宿主机A。运行脚本后,文件将被拷贝到宿主机B的指定目录中。

ME【纠正一】:

我以为 AI 把文件名理解错了,于是做了第一次“纠正”。

宿主机上的文件名不是 ids_method_costs_*.txt,而是 一系列文件,文件名为 ids_method_costs_yyyyMMddHHmmssSSSS.txt 比如 ids_method_costs_20230719122500042.txt

ChatGPT3【回答】:

如果宿主机上的文件名不是以"ids_method_costs_"开头并以".txt"结尾,而是包含日期和时间的字符串作为文件名的一部分,你可以使用shell脚本中的通配符来匹配这些文件名的模式。以下是更新后的示例脚本:

#!/bin/bash

# 指定宿主机A的IP地址和登录凭证
HOST_A="IP_ADDRESS_OF_HOST_A"
USER_A="USERNAME_OF_HOST_A"
PASSWORD_A="PASSWORD_OF_HOST_A"

# 指定宿主机B上存放文件的目录
HOST_B_DIR="/path/to/host_b/directory"

# 指定宿主机A上存放文件的目录
HOST_A_DIR="/path/to/host_a/directory"

# 拷贝文件
for file in ${HOST_A_DIR}/ids_method_costs_*; do
    filename=$(basename ${file})
    sshpass -p ${PASSWORD_A} scp ${USER_A}@${HOST_A}:${file} ${HOST_B_DIR}/${filename}
done

在更新后的脚本中,文件名模式的部分被移除,只使用通配符匹配文件名的前缀。脚本会遍历宿主机A上的文件列表,并将匹配到的文件拷贝到宿主机B的指定目录中。

确保在宿主机B上运行该脚本时已经安装了`sshpass`工具,并且能够通过SSH连接到宿主机A。运行脚本后,文件将被拷贝到宿主机B的指定目录中。请注意,拷贝前请务必确认宿主机A上的文件名匹配规则和目标文件夹的准确性。

ME【尝试后,纠正】:

看着似乎没有问题,执行了下,报错。于是又仔细看了下脚本,发现获取文件名列表似乎有点问题。

for file in ${HOST_A_DIR}/ids_method_costs_* 这个不对吧,这时候还没有登录 A 呢,怎么知道这上面的文件?

ChatGP3:

非常抱歉,之前的解答有误。如你所说,当无法直接登录宿主机A时,不能直接在宿主机B上使用通配符来获取文件列表。在这种情况下,你可以通过SSH连接宿主机A来执行命令来获取文件列表,并将结果传递给宿主机B。以下是一个修正后的脚本示例:

#!/bin/bash

# 指定宿主机A的IP地址和登录凭证
HOST_A="IP_ADDRESS_OF_HOST_A"
USER_A="USERNAME_OF_HOST_A"
PASSWORD_A="PASSWORD_OF_HOST_A"

# 指定宿主机B上存放文件的目录
HOST_B_DIR="/path/to/host_b/directory"

# 指定宿主机A上存放文件的目录
HOST_A_DIR="/path/to/host_a/directory"

# 通过SSH连接到宿主机A,并获取文件列表
file_list=$(sshpass -p ${PASSWORD_A} ssh ${USER_A}@${HOST_A} "ls ${HOST_A_DIR}/ids_method_costs_*")

# 拷贝文件
for file in ${file_list}; do
    filename=$(basename ${file})
    sshpass -p ${PASSWORD_A} scp ${USER_A}@${HOST_A}:${file} ${HOST_B_DIR}/${filename}
done

在修正后的脚本中,通过SSH连接到宿主机A并执行命令`ls ${HOST_A_DIR}/ids_method_costs_*`来获取宿主机A上符合指定模式的文件列表。然后,脚本将获取到的文件列表传递给宿主机B,并按照之前的方式拷贝文件。

确保在宿主机B上执行该脚本时已经安装了`sshpass`工具,并且宿主机B能够通过SSH连接到宿主机A。运行脚本后,文件将被拷贝到宿主机B的指定目录中。请注意,在运行该脚本之前,请确保正确配置了SSH连接和登录凭证,并验证宿主机A上文件位置和匹配模式的准确性。

最后修改后的脚本:

#!/bin/bash

# 指定宿主机A的IP地址和登录凭证
HOST_A="w.x.y.z"
USER_A="user"
PASSWORD_A="password"

# 指定宿主机B上存放文件的目录
HOST_B_DIR="/Users/xxx/Development/"

# 指定宿主机A上存放文件的目录
HOST_A_DIR="/tmp/"

file_list=$(sshpass -p ${PASSWORD_A} ssh ${USER_A}@${HOST_A} "ls ${HOST_A_DIR}/ids_method_costs_*")


# 拷贝文件
for file in ${file_list}; do
    filename=$(basename ${file})
    echo "sshpass -p ${PASSWORD_A} scp ${USER_A}@${HOST_A}:${file} ${HOST_B_DIR}/${filename}"
    sshpass -p ${PASSWORD_A} scp ${USER_A}@${HOST_A}:${file} ${HOST_B_DIR}/${filename}
done

一次拷贝解决


以上两个步骤,是我做了任务拆分,分成了两个脚本、两次拷贝过程。那么,是否只需要一个脚本就可以了呢 ? 于是我又询问了下 AI。

ME【提问】:

在宿主机 A 的 k8s 上有一个 pod deploy-ids-detect-7b777ff97b-ncrqb , 这个 pod 下 有一些文件,文件命名为 ids_method_costs_yyyyMMddHHmmssSSSS.txt,比如 ids_method_costs_20230719122500042.txt , 将这些文件批量拷贝到宿主机 B 上。 A 不能 ping 通 B, B 能 ping 通 A ,脚本需要在 A 上执行。

ChatGPT3【回答】:

你可以在宿主机 A 上使用 kubectl 命令和 scp 命令来完成这个任务。下面是一个示例脚本,可以用于批量拷贝文件:


#!/bin/bash

# 定义宿主机 A 和宿主机 B 的 IP 地址
HOST_A="IP_A"
HOST_B="IP_B"

# 获取 pod 名称
POD_NAME=$(kubectl get pods -o=jsonpath='{.items[?(@.metadata.labels.app=="deploy-ids-detect")].metadata.name}')

# 获取文件列表
FILES=$(kubectl exec $POD_NAME -- ls /path/to/files/ids_method_costs_*.txt)

# 遍历文件列表
for FILE in $FILES; do
  # 拷贝文件到宿主机 B
  scp user@$HOST_A:"$FILE" user@$HOST_B:/path/to/destination/
done

请将 IP_A 替换为宿主机 A 的 IP 地址,将 IP_B 替换为宿主机 B 的 IP 地址。然后指定正确的路径和用户名,执行该脚本即可将文件批量拷贝到宿主机 B 上。

请注意,该脚本假设你已经正确安装和配置了 kubectlscp,并且具有足够的权限执行这些操作。

注意,这里我提问错了。 脚本应当在 B 上执行。读者可以尝试修改下,转成实用的脚本。或者自行提问尝试下。

经验小结

  • 与 AI 对话时,一定要明确上下文语境。AI 虽然能写代码,但它无法感知上下文语境,这是它最大的弱点,需要帮助它补齐。
  • 尽可能明确约束。比如,B 能 ping 通 A,但 A 不能 ping 通 B 。脚本要在 B 上执行。不明确这点,AI 很可能写一个在 A 上运行的脚本。这又不符合了。
  • 具体再具体。要尽可能把相关要素说清楚,比如文件名是怎样的,在哪个目录下。不然,只要有一点不明确, AI 就有自由发挥的余地啦!
  • 最后,要对 AI 写出的脚本做一点更改润色,方便调试,比如写一点 echo 或 print 语句。

在这个过程中,又学到了一点知识。在这个场景中,我知道如何拷贝单个文件,但不知道如何获取在一台机器上获取另一台机器的指定环境的多个文件。于是,借 AI 的能力,解决了这个知识盲点。

由于生成式 AI 最初是在计算机领域产生的,因此, AI 对于计算机编程是非常适配的。你几乎只要说明 shell 或者 python ,它就会用指定语言来实现。即使你不指定,那么它可能会从 Python 或者 Java 或者 Shell 自动选择一种(视简单而定)。

这既有益处也有不利。益处在于,它对编程工作的助益是非常有效的;不利在于,它在未来大概率会取代初中级编程工作,对只会写代码的初中级工程师形成降维打击。不过,正如在之前谈到的,如果一个程序员不仅仅局限于写代码,还具备良好的代码品鉴能力,掌握了良好的设计实践和生产环境的稀缺经验,对需求有良好的理解分析能力,具有良好的全局观和问题处理经验,这样 AI 是很难取代的。因为 AI 不理解语境。

换句话说,在明确语境下,AI 是几乎可以完全取代知识层面的工作的,但它暂时还不适合做语境结合分析。这是人应当去发展的一项技能优势。

PS: 日常我们说话,大多是废话,轻飘飘地像烟云散去,不着痕迹。然而,用计算机语言写下的句子,却是能够执行而产生多样而“神奇”的效果的。这大概就是计算机编程迷人的一面吧。

posted @ 2023-07-22 04:06  琴水玉  阅读(131)  评论(0编辑  收藏  举报