程式狂想筆記

一個攻城師奮鬥史

0%

Jenkins 執行 Java 程式亂碼解決方法

最近公司上板 Java 排程程式都會遇到亂碼 ????
這個都不能手動執行
今天解決這個問題
整理一下

首先,簡單講一下
我們 jenkins 上板程式是透過 ssh 打指令操作

這邊我備註一下,我們公司上板 Java 程式,是排程程式,這些排程不是使用 Crontab 方式去執行
所以這邊執行都是透過 shell 腳本方式去重複執行

有在家目錄使用socure ~/.profile
但是這個過程
奇怪的是 locale 查看出來的 LC_CTYPE=en_US.UTF8 其他都是 POSIX
一般用 Xshell 登入是 locale 全看到 en_US.UTF8

這邊我還不確定是什麼原因?

non-interactive + non-login shell
最後一種模式為非交互非登陸的shell,創建這種shell典型有兩種方式:
bash script.sh
ssh user@remote command
這兩種都是創建一個shell,執行完腳本之後便退出,不再需要與用戶交互。

ssh連接遠程主機執行腳本的環境變量問題_whitehack的專欄-CSDN博客_ssh 遠程執行命令 含變量 備份圖
目前看到這篇有疑似這個問題
所以導致登入環境變數會有不一樣結果
我也沒看到有設定 BASH_ENV 參數

不過確實因為 ssh remote command 登入
造就登入不一樣結果

裡面有滿清楚內容ssh連接遠程主機執行腳本的環境變量問題_whitehack的專欄-CSDN博客_ssh 遠程執行命令 含變量關於 Linux 下 Bash 與 Zsh 啟動檔的載入順序研究 | The Will Will Web

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
+----------------+--------+-----------+---------------+
| | login |interactive|non-interactive|
| | |non-login |non-login |
+----------------+--------+-----------+---------------+
|/etc/profile | A | | |
+----------------+--------+-----------+---------------+
|/etc/bash.bashrc| | A | |
+----------------+--------+-----------+---------------+
|~/.bashrc | | B | |
+----------------+--------+-----------+---------------+
|~/.bash_profile | B1 | | |
+----------------+--------+-----------+---------------+
|~/.bash_login | B2 | | |
+----------------+--------+-----------+---------------+
|~/.profile | B3 | | |
+----------------+--------+-----------+---------------+
|BASH_ENV | | | A |
+----------------+--------+-----------+---------------+

配置文件建議

回顧一下前面提到的所有配置文件,總共有以下幾種:

/etc/profile
~/.bash_profile
~/.bash_login
~/.profile
/etc/bash.bashrc
~/.bashrc
$BASH_ENV
$ENV

不知你是否會有疑問,這麼多的配置文件,究竟每個文件裡面應該包含哪些配置,比如PATH應該在哪?提示符應該在哪配置?啟動的程序應該在哪?等等。所以在文章的最後,我蒐羅了一些最佳實踐供各位參考。(這裡只討論屬於用戶個人的配置文件)

~/.bash_profile:應該儘可能的簡單,通常會在最後加載.profile和.bashrc(注意順序)
~/.bash_login:在前面討論過,別用它
~/.profile:此文件用於login shell,所有你想在整個用戶會話期間都有效的內容都應該放置於此,比如啟動進程,環境變量等
~/.bashrc:只放置與bash有關的命令,所有與交互有關的命令都應該出現在此,比如bash的補全、alias、顏色、提示符等等。特別注意:別在這裡輸出任何內容(我們前面只是為了演示,別學我哈)

因為我猜測 jenkins 進去跟我用 xshell 語系不一樣
所以就做了 log 紀錄,發現真的不一樣

1
2
3
4
5
6
export LC_ALL="en_US.UTF-8"
date >> /tmp/test_ssh.log
env >> /tmp/test_ssh.log
echo "--------" >> /tmp/test_ssh.log
locale >> /tmp/test_ssh.log
echo "=======" >> /tmp/test_ssh.log

但不是上面問題

之前看到使用 file.enconding 去解決
但實際用上卻沒有效果
跟 file.enconding 沒關係,是我使用 JAVA_OPTS環境變數
沒有效果,後面會講到

java 預設在處理文字檔案的寫入時,
會以 os 的預設編碼當作寫入的編碼規則,
這個問題常會發生在 windows 英文的作業系統要寫入中文的時候發生,

解法1:
如果是用 java 執行的 jar ,
java -Dfile.encoding=UTF8 -jar e:\bin\ExportToFile.jar

解法2:
加入環境變數 JAVA_OPTS,
windows 環境:
JAVA_OPTS=-Dfile.encoding=UTF8;%JAVA_OPTS%
linux 環境:
$> export JAVA_OPTS=-Dfile.encoding=UTF8:$JAVA_OPTS

解法3:
在程式裡面設定:
取得目前系統檔案編碼屬性 String defaultEncodingName = System.getProperty( “file.encoding” );
設定系統檔案編碼屬性 System.setProperty(“file.encoding”, “UTF-8”);
解決 java 寫檔的中文亂碼問題 @ caffeine :: 隨意窩 Xuite日誌

了解一下 LC_* 環境變數

wiki

在Ubuntu作業系統中,使用man locale-gen或man locale可獲得關於locale實現的細節。實際上是由glibc庫實現的。

locale相關(環境)變數生效的優先順序:[1]

LANGUAGE 指定個人對語言環境值的主次偏好,例如zh_CN:en_US:en
LC_ALL 這不是一個環境變數,是一個可被C語言庫函式setlocale設定的宏,其值可覆蓋所有其他的locale設定。因此> 預設時此值為空
LC_xxx 可設定locale各方面(category)的值,可以覆蓋LANG的值。
LANG 指定預設使用的locale值

可以把上述環境變數設在/etc/profile 或 /etc/environment等系統初始檔案中。值得注意的是,若LANG或LC_ALL被設> 定為 “C”,那麼LANGUAGE的值將被忽視。 [2]

除 C 和 POSIX這兩個locale名稱外,locale的名稱並未標準化。Linux平台與Windows系統的locale名稱有很大不同。> Linux名稱的命名規則為:

language[_territory[.codeset]][@modifier]

其中language是ISO 639-1標準中定義的雙字母的語言代碼

暫時語系用法

感覺是暫時環境變數,但沒有詳細看到有人這樣介紹

$ LC_TIME=en_US.UTF-8 date
Fri Oct 31 19:51:16 CST 2014
$ LC_TIME=fi_FI.UTF-8 date
pe 31.10.2014 19.52.00 +0800
$ LC_TIME=zh_CN.UTF-8 date
2014年 10月 31日 星期五 19:53:07 CST

PS: 上面使用 locale -a 有安裝成功的才能用

POSIX 和 C

更改 Linux 系統語言環境變量 - Jamin Zhang

LC_ALL=C 是為了去除所有本地化的設置,讓命令能正確執行。
C 是系統默認的 locale,」POSIX」是」C」的別名。所以當我們新安裝完一個系統時,默認的 locale 就是 C 或 POSIX。

to default locale: No such file or directory

Locale - Ubuntu 18.04 探索筆記

會多出現三行

ssh remote bash 登入使用 locale 指令會出現

locale: Cannot set LC_CTYPE to default locale: No such file or directory
locale: Cannot set LC_MESSAGES to default locale: No such file or directory
locale: Cannot set LC_ALL to default locale: No such file or directory

只要執行下面的指令,就可以解決

$ sudo locale-gen zh_TW.UTF-8

但我沒使用這個方案解決

至於原因可能是
Linux Xshell登录报错 locale: Cannot set LC_CTYPE to default locale: No such file or directory(已解决)_醉世老翁的博客-CSDN博客_locale -a locale: cannot set lc_ctype to default l
CentOS6.5手动安装glibc-2.14后locale出现No such file or directory终极解决办法_guitar___的博客-CSDN博客_centos character map file `utf-8 not found: no su

查看 Java file encoding

Java May Use UTF-8 as its Default Charset - DZone Java

CharsetDemo.java

CharsetDemo.java
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
public class CharsetDemo
{
/**
* Supplies the default encoding without using Charset.defaultCharset()
* and without accessing System.getProperty("file.encoding").
*
* @return Default encoding (default charset).
*/
public static String getEncoding()
{
final byte [] bytes = {'D'};
final InputStream inputStream = new ByteArrayInputStream(bytes);
final InputStreamReader reader = new InputStreamReader(inputStream);
final String encoding = reader.getEncoding();
return encoding;
}

public static void main(final String[] arguments)
{
out.println("Default Locale: " + Locale.getDefault());
out.println("Default Charset: " + Charset.defaultCharset());
out.println("file.encoding: " + System.getProperty("file.encoding"));
out.println("sun.jnu.encoding: " + System.getProperty("sun.jnu.encoding"));
out.println("Default Encoding: " + getEncoding());
}
}

環境變量JAVA_TOOL_OPTIONS、_JAVA_OPTIONS、JAVA_OPTS設置_ly199108171231的博客-CSDN博客_java_options 備份圖
這邊查到 JAVA_OPTS 是 Tomcat 用的
難怪我試個老半天都沒有什麼效果
JAVA_TOOL_OPTIONS 確實就正常了

JAVA_OPTS:常用於一些應用的配置,如Tomcat,但它一般不作為環境變量,也不能被JVM識別的,是那些應用的自定義配置;
_JAVA_OPTIONS:也是作為環境變量來替代命令行參數的,但它是JVM廠家自定義的,可以覆蓋JAVA_TOOL_OPTIONS,但各廠家的不同,_JAVA_OPTIONS是Oracle的JVM,而IBM的則是用IBM_JAVA_OPTIONS。
JAVA_TOOL_OPTIONS:是標準的,所有虛擬機都能識別和應用的。

如果想驗證上面的不同也不難,如果設置了JVM能識別的環境變量,JVM會有”Picked up”的提示的,如:

export JAVA_OPTS=zhaiqiafneng
export JAVA_TOOL_OPTIONS="-Xmx512m -Xms64m" 

java -version 

JVM會打印:

Picked up JAVA_TOOL_OPTIONS: -Xmx512m -Xms64m
java version "1.8.0_101"
Java(TM) SE Runtime Environment (build 1.8.0_101-b13)
Java HotSpot(TM) 64-Bit Server VM (build 25.101-b13, mixed mode)
1
2
3
4
5
6
# Big5
LC_ALL=zh_TW.BIG5 java Xxxx
# Big5
java -Dfile.encoding=big5 Xxxx
# Big5
LC_ALL=zh_TW.UTF8 java -Dfile.encoding=big5 Xxxx

排程踩雷注意
linux 中crontab 定时 shell脚本启动jar包,tomcat失败_醉世老翁的博客-CSDN博客_linux 定时任务启动的tomcat jar包不一样

Java properties and XML and log4j

固定都吃 ISO-8859-1
.properties檔案的編碼是ISO-8859-1,又稱為Latin-1。所有非Latin-1字元必須利用Unicode跳脫字元錄入,例如\uHHHH中,HHHH是某個字元的Unicode字元集的十六進位的索引。這樣就可以使用.properties檔案作為在地化的屬性資源包。非Latin-1的文字檔案,可以通過使用隨JDK提供的native2ascii工具轉換為正確的.properties檔案;或使用如po2prop[1]的第三方工具,管理雙語在地化格式跳脫為.properties

難怪… .properties 無法正常看到 UTF-8

.properties - 維基百科,自由的百科全書

XML 是吃 UTF-8

原本想獨立寫一篇測試
但內容不太多,所以就整理在這邊

log4j 也能設定指定 encoding,不過…這邊跟 Java 一樣,預設是帶 System 帶入的
log4j日誌記錄到文件 - Log4j教學

相關網站整理

裡面也混雜一些彩蛋

其他連結(很多重複)